---
name: discover
description: Create or extend a persistent Apilift interface from current documented APIs or, when unavailable, observed logged-in UI behavior. Use for a new service, missing CLI coverage, or a correction to an existing operation. Publish service metadata, entity metadata, service auth/transport recipes, and each operation as independent resources.
---

# Discover or extend a service

Turn only the requested product outcome into individually saved consumer commands. Remain the sole discovery owner: do not delegate documentation assessment, browser actions, candidate selection, schema judgment, execution, cleanup, or saving. Do not inspect Apilift source, another workspace, browser storage, credentials, or raw captures as an answer key; the CLI is the discovery interface. Never ask for, read, paste, or store a user's API credential: the dashboard and extension own local provisioning.

Use `apilift` for every Apilift call. Treat sandbox, network, registry, executable, and browser-control failures as environment failures, not service evidence.

## Open the app and resume from Registry state

Run `apilift discover open <app> [--origin URL] [--service-id ID]`, inspect the Registry head, and run `apilift <app> gaps` before opening a draft or acting in the product. Read the authoritative `notice`, `serviceId`, and `visibility` aloud to the user before product access: private means resources are saved only to the active workspace, while Community means they are public. Use `--service-id` only when the user explicitly selected a Community app instead of the workspace-private app that automatic origin lookup prefers. Do not silently choose or override visibility, infer it from another workspace, or change scope during the session.

There are two paths. A new app follows metadata → auth when needed → entity → Action, publishing each resource before opening the next draft. Extending an existing app reuses its published metadata, auth recipes, and entities unless the requested coverage requires changing or adding one of them. Before opening any draft, inspect the requested Action and its dependencies: if the entity and published auth recipe already exist, draft the Action directly; if exactly one dependency is missing or changing, author and publish only that dependency first. Never recreate or republish unchanged scaffolding. The app `origin` is its primary UI location, identity, icon/domain source, and default browser start location; each auth recipe independently owns its browser context or direct base URL and `allowedOrigins`, which may differ from the UI origin.

For new metadata, select exactly one category by the app's primary positioning. The allowed category slugs are `ai`, `communication`, `sales-crm`, `marketing`, `customer-support`, `productivity`, `content-files`, `commerce`, `finance-accounting`, `data-analytics`, `developer-it`, `people-learning`, `business-operations`, `legal-risk-compliance`, and `other`; reserve `ai` for general-purpose AI products, classify task-specific AI products by their primary work category, and use `other` only when none fits.

`discover open` is documentation-first and must not request a Chrome lease or browser preflight. Read its `apiDiscovery` assessment and returned `next` instruction before deciding whether UI capture is necessary. Explicit documentation supplied by the user always overrides a saved negative assessment.

Use `apilift discover <command> --help` only when the happy path or returned single `next` instruction is insufficient. Use `drafts` only to diagnose local drafts and their owners; every row is `publicationState: local-draft` and `coverage: false`. One session may own exactly one draft; `draft` on an existing unowned Action claims it only when the session owns none and never switches among drafts. Finish, discard, or disconnect before claiming another resource. A refresh must preserve the CLI-created backup. Never edit Registry state directly.

## Prefer the documented API

Use this discovery priority exactly for each requested Action: current documented API, then UI capture, then an official MCP, CLI, or SDK. Before any browser preflight, complete this API gate in order without skipping a later step because an earlier source looked authoritative:

1. Find a current official OpenAPI 3.0 or 3.1 document. When one exists, run `apilift discover api import <openapi-url-or-file>` and `api search <query>` for the requested Action; import is an operation index, never permission to publish the whole API.
2. If no OpenAPI document exists, search the current official endpoint reference and prose API documentation for an operation that produces the requested outcome. Absence from OpenAPI is not evidence that the documented API lacks the operation.
3. Once an operation is found, separately determine how a user can authorize it and obtain or grant that access. Check current official authentication guides, developer or app documentation, account or workspace access-token settings/help, endpoint requirements, and official examples; treat these as complementary evidence rather than assuming OpenAPI is exhaustive.
4. If the imported operation reports no `authRecipes`, an `unsupported` authorization status, or only a mechanism the direct runtime cannot execute, do not begin UI fallback. This result means authorization discovery is unfinished: search the official sources from step 3 for every alternative access method the service supports, without privileging one credential type or stopping at the scheme named by OpenAPI.
5. If any documented operation plus authorization and access-provisioning path is executable by the current direct runtime, author the service recipe from those official sources and create the Action through the documented API. For prose-only operation documentation use `apilift discover draft <entity>.<verb> --docs <url> --auth-recipe <name>`, and for an imported OpenAPI operation `apilift discover draft <entity>.<verb> --from-api <operation-id> --auth-recipe <name>`; when OpenAPI omitted the usable authorization option, combine its operation contract with the separately documented service recipe instead of discarding the operation.
6. Begin UI fallback only for a requested Action after the preceding steps found no complete usable documented API path. Keep direct recipes and Actions already found for other requested Actions; one uncovered Action never sends the whole service to UI.

Do not claim that an API requires one authorization mechanism merely because OpenAPI lists only that mechanism. Do not treat importer fields such as `unsupportedSecuritySchemes`, `authorizationStatus`, or empty `authRecipes` as a completed assessment; they describe only the imported document and trigger step 3. A saved negative assessment is reusable only when its checked sources prove that both operation discovery and alternative authorization discovery completed under this gate.

The service auth resource is also the service transport model. Each named recipe owns `transportKind`, allowed origins, execution parameters, authorization injection, and credential acquisition; `transportKind` must explicitly be `browser` or `direct`. An Action selects only `authRecipe`, or inherits `defaultRecipe`; never add transport, visibility, credentials, or acquisition fields to an Action. Public/private visibility belongs only to the service and applies uniformly to all its recipes.

For a credential recipe, research current official sources first, then replace importer placeholders with a real public HTTPS acquisition URL and exact nontechnical steps the user follows to obtain or grant access. Do not invent steps. The auth run will refuse generic or missing instructions. A direct API with no authorization still needs a direct recipe with no credential fields. Never reinterpret an unsupported mechanism as another credential; use an alternative only when a separate official source documents it.

Credential acquisition metadata is documentation, not evidence that this device lacks the credential and not a user-action boundary. Continue with the CLI's exact auth `discover run`; it checks the operating-system vault without exposing the value. Stop for the user only when that run returns the structured `credential-required` action, never merely because an imported or edited recipe declares credential fields or acquisition steps.

Record a service-level negative assessment with `apilift discover api unavailable --status not-found|unsupported --checked <url>... --reason <text>` only when the completed gate establishes that the service has no usable documented API at all. Its checked sources must include the operation/reference search and, whenever any API or unsupported authorization was found, the separate alternative-authorization search. A service with a usable API that omits or cannot execute one requested Action remains API-available: keep its direct recipes and use UI fallback only for that uncovered Action. A fresh adequately sourced service-level negative assessment lasts 30 days and moves directly to UI discovery; an expired, absent, or inadequately sourced assessment requires a new search. Do not treat network, sandbox, or search failure as evidence that an API does not exist. If UI discovery also cannot cover the outcome, check an official MCP, CLI, or SDK last and use it instead when it covers the task.

Before the first UI action only, prove ordinary Chrome control with `apilift browser status --agent codex|claude --json` and execute returned machine repairs yourself. Ask the user only for a browser approval, installation or reload, sign-in, MFA, CAPTCHA, or WebAuthn step the agent cannot perform. Chrome is the only browser Apilift can record in; a failed Chrome preflight is a stop, not permission to drive another browser.

## Validate and publish auth before its Actions

An unpublished local auth draft has no execution or coverage effect. While the session owns an auth draft, validate each new or changed recipe with `apilift discover run --api-operation <id> --auth-recipe <name> [arguments]` for a documented safe request or `apilift discover capture start`, one safe UI request, `capture stop`, then `apilift discover run --candidate <id> --auth-recipe <name>`. Prefer a read/status request. Each receipt must cover every declared request origin against the current recipe hash; then run `apilift discover finish auth`. Only after that succeeds may an Action select the recipe. If the CLI reports `auth-recipe-not-published`, run the exact auth validation and finish commands it returns; do not patch around the ordering rule.

When auth validation returns `credential-required`, stop immediately and send its `userMessage` as the complete request to the user. It contains researched steps to obtain the key, the exact Apilift setup link, and the precise retry command. The user must open the link, choose **Configure credential**, paste the key only into the Apilift extension page, select **Save credential**, and return to the agent. Never create, request in chat, read, copy, paste, inspect, or retain the key yourself. After the user confirms, run only the returned retry command, then finish auth. Do not synthesize or shorten this handoff from acquisition metadata: only the CLI-created action proves the credential is absent and provides a valid value-free setup intent.

## Capture one semantic Action

Create the Action with `apilift discover draft <entity>.<verb>` only after its published entity and selected published auth recipe exist; the same command reopens an unowned disconnected draft while this session owns no draft. Before capture, decide the effect, caller-visible success, inputs, synthetic examples, and proof. Perform a metadata-only review using only the app title/description, entity name/title/description, and Action verb/description: those fields alone must identify the domain resource, effect, scope, and returned result without a route, UI label, endpoint name, or implementation term. Rename or rewrite ambiguous or contradictory metadata before capture and repeat this blind review before finish. Use domain nouns and verbs, and do not add neighboring CRUD verbs outside the requested outcome.

Explore the UI before recording. Immediately before the single foreground action, run `apilift discover capture start` as a standalone command and wait until that exact invocation finishes with an explicit successful `state: recording`; when the caller genuinely chooses values for the action, declare them before the UI action in the one structured option `--inputs '[{"name":"query","value":"example","description":"Search text chosen by the caller","required":true}]'`. Never declare an app-generated identifier, hidden scope, ambient field, response value, or value first learned during the action as an input. A running process, timeout, empty output, draft result, or assumed background continuation is not an armed capture and forbids the UI action. Its `browserTarget` is the only tab in which the action may run: bind browser control to that already-open tab by its returned tab identity, and never create, navigate, or use another same-origin tab for the action. Perform the action once only after that acknowledgement, then run `apilift discover capture stop`. Choose from the returned ranked candidates. Use `capture list --all` only when the expected request is absent, and `details` only when the listed method, URL, status, and sizes do not identify it. Repair any reported pending request, wrong tab, loss, truncation, or stale browser state before repeating an effect. A failed or timed-out `capture stop` or `capture cancel` did not close anything unless a later retry returns the matching confirmed transaction; never continue to another capture while the broker still owns that window.

Run `apilift discover mark <candidate>`. Mark creates the full structural `operation.json` and a separate local observation, then prints only a bounded edit handoff; inspect the reported operation file rather than expecting captured payloads on stdout. It changes only the active operation. Re-mark only when the chosen request was wrong or new wire evidence is necessary; it invalidates the current run proof.

Edit the printed operation file directly or use one merge patch. Keep only entity, verb, description, effect, semantic request, response, proof, and an optional `authRecipe` selector. Preserve every generated request marker exactly as `{{$bindings.<name>}}`; syntax such as `{{.<name>}}` is not supported. For every public request parameter choose exactly `{"kind":"caller"}` when the caller arbitrarily decides the value, or `{"kind":"action","action":"entity.verb","path":"/*/id"}` when the value must be selected from another published Action's selected and projected result; the empty path selects a scalar producer result. Apilift never runs the producer automatically: help and missing-argument diagnostics tell the consumer which Action to run and the consumer explicitly chooses one returned value. Use `omit`, `constant`, and `generated` only for non-caller request mechanisms. Stable visible app or workspace scope is an app-owned context variable: define its type, description, and `caller` or Action source once in app metadata, then let each consuming Action carry only `{"source":{"kind":"context","context":"workspace-id"},"required":true}`; never duplicate context type, prose, provenance, or a captured account value inside an Action. If a request identifier is generated client-side, declare one generated binding with its prefix and select that exact successful request value as a scalar result using `{"kind":"scalar","source":{"kind":"request-binding","binding":"case-id"}}`; do not invent a second result value or combine generated selection with a path, pagination, projection, or disabled branches. Captured dynamic values not genuinely predeclared with `--inputs` are immutable `mustDiscover` requirements keyed to their wire paths; relabelling one as caller will continue to fail, so discover and finish the exact producer Action or recapture with genuine prior caller input evidence. Finish validates same-app producer callability, projected non-disabled path, compatible type, context availability, and an acyclic dependency graph. Captured values never become executable constants, defaults, examples, or enums, and a marked binding must not be removed or replaced by its observed literal. Never extend app-context treatment to opaque identifiers. Every caller input and visible response property needs concise consumer-facing documentation. Use `response.projection.include` with JSON Pointer paths such as `/id` and `/items/*/id` to retain a small semantic result; use `x-apilift-disabled: true` only to remove a few known noisy or sensitive branches.

## Let browser auth validation prove the fallback mechanism

Start with an auth draft and one safe read candidate. Capture is allowed while auth is the active draft so a browser recipe can be proven before an Action exists. Operations never contain credentials, cookies, browser transport headers, locators, browser context, build identifiers, or injection rules; they may select one named published recipe when the app default is not correct.

One browser recipe represents one reusable browser context, allowed-origin set, and injection mechanism across methods and paths. Do not duplicate recipes by HTTP method or endpoint. Use `auth explain` only to choose among unresolved value-free sources, and `auth primer` only when the CLI says automatic mapping found no stable source and one deterministic read-only navigation or selector action can acquire it. Mapping never replays a mutation. Do not replace a direct recipe with a browser recipe merely because its local credential is not configured.

## Run, prove, and finish one resource

Run `apilift discover run [<entity>.<verb>] [consumer flags]`. This is the real consumer runtime: it resolves the Action's selected service recipe and dispatches either direct or browser transport; browser setup, implicit browser credentials, recipe acquisition, refresh, and one safe auth retry remain automatic, and captured observations are unavailable to execution. A direct credential is configured by the user from the service's dashboard instructions on the extension-origin prompt and stays in the operating-system vault; never suggest an environment variable, terminal setup, browser storage, file, or server-side secret. Treat `credential_missing`, `vault_locked`, `vault_unavailable`, and `credential_contract_changed` as exact local provisioning outcomes. Never perform preparatory UI traffic or copy session material. Do not repeat an unchanged mutation when the CLI says it may have reached the service.

A read must return schema-matching success. A create, update, delete, or action also needs response assertions, readback through another runnable operation, or absence proof. HTTP acceptance or an echoed input alone is not proof. Deletion normally uses an absence readback; asynchronous behavior needs later state evidence.

No credential recipe can be published without a successful current run. Because the auth draft is the session's only draft, credential provisioning happens as soon as that validated dependency is required and before any dependent Action draft is opened. Use only the CLI-provided value-free setup intent and exact user instructions.

Finish exactly one resource with `discover finish service`, `finish auth`, `finish entity <name>`, or `finish <entity>.<verb>`. Describe a private result as saved to the workspace, never published; reserve published for Community. Operation finish requires a current semantic run. Use `--skip-run --reason` only for a structurally complete unverified command. Use `discover defer --reason` for a retryable local blocker. Publish `--unavailable no-route|user-action --reason` only for terminal command-scoped evidence; auth loss, plan, role, quota, missing state, browser trouble, ambiguous UI, or an unexamined control are not terminal evidence.

After each Action finish, run `apilift <app> gaps` and cite only its published Registry result as coverage. Never count draft files, successful local runs, or rejected finishes. Inspect the consumer help and execute the ordinary consumer command once. Before piping or parsing its result with `jq`, a script, or another machine reader, pass `--json`; ordinary output is human-readable and may carry tables, pagination hints, counts, or spend information. Treat `warning [large-result]` during discovery as a required response-design review, not a finish blocker: first narrow `response.select`, add `response.projection.include`, or disable known noise. If the result is inherently large, use only the bounded preview and reported byte/item counts in context, then search the private complete artifact with the copyable `rg --fixed-strings` or `jq` commands from the `apilift.large-result/v1` envelope; never paste the full artifact into the conversation.

## Keep scope and recovery disciplined

Every confirmed `capture stop` or `capture cancel` and every completed browser run withdraws its extension scope. Before ending or abandoning discovery, run `apilift disconnect <app>`; it stops this session's discovery runtime, withdraws its scope, preserves and releases its draft, clears selection, and releases the consumer browser connection when no live peer session still needs it. If a command fails, follow its one returned recovery action. Never infer that closing a Chrome tab ends discovery.

Create, modify, or delete disposable state already authorized by the user without asking again. Do not touch pre-existing state outside that scope. When the product necessarily autosaves a provider-generated placeholder as the first inseparable step of an authorized creation, claim only that newly created object and rename the owned object immediately into the user's allowed scope; this transient placeholder does not make the Action `user-action` or justify abandoning callable coverage. Make at most one optional cleanup attempt; cleanup is not a publication prerequisite unless delete is the operation being covered.

Follow deterministic CLI repair instructions instead of inspecting or improvising with implementation internals. Report a genuinely unmodeled CLI defect once with `apilift feedback '<concise sentence>'`, without secrets, payloads, URLs, opaque identifiers, or customer content, then continue the same workspace when possible.

If preflight reports `private-service-plan-required`, stop before product access and present only its upgrade and Make public actions. Never use a cached snapshot, local draft, raw HTTP, or another workspace's similarly named interface to continue around the entitlement decision. A service that is absent from the current workspace and Community is absent; never describe a foreign private interface as found, inaccessible, conflicting, or already existing.
