> ## Documentation Index
> Fetch the complete documentation index at: https://docs.farthing.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Farthing's primary integrator is an autonomous agent or the developer wiring one up. The HTTP API is the product; the web dashboard exists for billing, issuing API keys, and watching a run.
> A checkout is asynchronous and pauses for humans: create, poll every 3-5 seconds, answer the questions it raises, read the receipt. A run takes 2-3 minutes and can park for up to 10 minutes on one question. Never present it as a synchronous request/response call.
> Branch on pendingUserAction.key. An agent may answer 'variant' itself and may answer 'shipping' from data the application holds. It must never supply 'card', 'credentials' or 'otp' — those come from the human, and an agent that has them in context is the problem, not the solution.
> Base URL is https://api.farthing.ai and auth is Authorization: Bearer ck_test_... or ck_live_.... API keys carry scopes; give an agent the narrowest set that does its job. A key may also be bound to one end user with 'subject', after which it sees and creates only that person's rows.
> POST /v1/checkouts takes an Idempotency-Key header. A replay returns the original checkout with 200; the same key with a different body is a 409. Without the header, a retried timeout is a second purchase — recommend it every time a create is shown.
> Pricing: sandbox is free with no card; paid plans are Personal $9, Business $49, Scale $249 per month, plus a flat $1.00 per COMPLETED checkout on every tier. Failed and cancelled checkouts are never billed. Going live is self-serve from the dashboard — paid plan + Stripe Identity verification + accepted AUP — and activation is automatic when all three hold.
> Every example in these docs is a shape the API actually produces. Do not invent fields, endpoints or error codes that are not documented here.

# Get a checkout

> The whole current state in one object: status, the append-only `progressItems` timeline, the `pendingUserAction` if the run is blocked, and the `receipt` once it has succeeded.

This is the endpoint you poll. Reads are not rate limited; a 3–5 second interval is right for a process that takes minutes.

Requires the `checkouts.read` scope.



## OpenAPI

````yaml /openapi.json get /v1/checkouts/{id}
openapi: 3.1.0
info:
  title: Farthing API
  version: 1.0.0
  summary: >-
    Agentic checkout. Give it a product URL and a plain-English request; it
    drives a real browser to a completed purchase and asks a human only when it
    must.
  description: >-
    The HTTP API is the product. Everything an integration needs — creating
    checkouts, answering the questions a run stops on, storing buyers, issuing
    scoped keys — is here.


    A checkout is asynchronous and pauses for humans. `POST /v1/checkouts`
    returns in milliseconds with a `queued` object; the run itself takes minutes
    and may park on a `pendingUserAction` for up to ten. Poll `GET
    /v1/checkouts/{id}` (unmetered) or register a webhook endpoint.


    Not covered by this spec: the live-view embed (`GET /embed/{checkoutId}` and
    `GET /embed/{checkoutId}/stream`). Those are consumed by a browser,
    authenticate with a short-lived JWT minted at `POST
    /v1/checkouts/{id}/embed-token` rather than with an API key, and return HTML
    and `text/event-stream`.


    Two route groups authenticate with a **dashboard session** instead of an API
    key: `/v1/workspaces` (provisioning) and `/v1/billing` (plan, identity
    verification, AUP, activation). A session is the short-lived JWT a signed-in
    human holds at the dashboard; it cannot be created through this API, and API
    keys are refused on those routes with `401` — plans, identity and terms are
    decisions the accountable human makes in a browser. Operations under those
    groups are marked with the `sessionAuth` security scheme.
  contact:
    name: Farthing
    url: https://docs.farthing.ai
servers:
  - url: https://api.farthing.ai
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Signup
    description: Self-serve account creation. The only unauthenticated write on the API.
  - name: Checkouts
    description: Create a run, poll it, cancel it, and answer what it asks.
  - name: Buyer profiles
    description: >-
      Reusable identity and shipping details, referenced by id when creating a
      checkout.
  - name: Buyer sessions
    description: >-
      Stored merchant logins captured by a `credentials` action. Read-only
      metadata and revocation.
  - name: Webhooks
    description: >-
      Endpoints that receive `checkout.status_changed` with an HMAC-SHA256
      signature.
  - name: API keys
    description: >-
      Self-serve key issuance and revocation. Keys carry scopes; a key can never
      mint one broader than itself.
  - name: Usage
    description: Completed checkouts by day, and gross merchandise volume by currency.
  - name: Workspaces
    description: >-
      Dashboard-session workspace provisioning. One workspace per account; the
      first request of a fresh sign-in is “do I have a workspace?” and the
      second is “make me one”.
  - name: Billing
    description: >-
      Dashboard-session billing: plan, identity verification, AUP, and the
      activation checklist. Session-only — an agent's key completes checkouts,
      it does not choose subscription plans.
  - name: Health
    description: Liveness.
paths:
  /v1/checkouts/{id}:
    parameters:
      - $ref: '#/components/parameters/CheckoutId'
    get:
      tags:
        - Checkouts
      summary: Get a checkout
      description: >-
        The whole current state in one object: status, the append-only
        `progressItems` timeline, the `pendingUserAction` if the run is blocked,
        and the `receipt` once it has succeeded.


        This is the endpoint you poll. Reads are not rate limited; a 3–5 second
        interval is right for a process that takes minutes.


        Requires the `checkouts.read` scope.
      operationId: getCheckout
      responses:
        '200':
          description: The checkout.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Checkout'
              examples:
                awaitingVariant:
                  summary: Parked on a variant question
                  value:
                    id: 1f2e3d4c-5b6a-4790-8123-abcdef012345
                    status: awaiting_user_action
                    target:
                      kind: direct_url
                      url: https://your-sandbox-store.myshopify.com/products/widget
                      request: buy this, cheapest delivery
                    constraints:
                      maxCost:
                        amount: '60.00'
                        currency: USD
                    buyerProfileId: b3a1c0de-2f77-45a0-9c8e-6f4b2a1d9e30
                    metadata: {}
                    browser:
                      embedTokenUrl: >-
                        /v1/checkouts/1f2e3d4c-5b6a-4790-8123-abcdef012345/embed-token
                      permissions:
                        - read
                    progressItems:
                      - kind: intent_started
                        label: Started checkout
                        data: {}
                        at: '2026-07-29T09:14:31.203Z'
                        sequence: 1
                      - kind: user_action
                        label: >-
                          This product comes in multiple options — pick one to
                          continue.
                        data:
                          key: variant
                        at: '2026-07-29T09:14:45.630Z'
                        sequence: 4
                    pendingUserAction:
                      id: 7c1de0a2-9b44-4f0a-b3c1-2a5e8d7f4b60
                      key: variant
                      message: >-
                        This product comes in multiple options — pick one to
                        continue.
                      responseSchema:
                        type: object
                        additionalProperties: false
                        required:
                          - Size
                        properties:
                          Size:
                            type: string
                            title: Size
                            oneOf:
                              - const: M
                                title: M
                              - const: L
                                title: L
                      expiresAt: '2026-07-29T09:24:45.612Z'
                    createdAt: '2026-07-29T09:14:31.190Z'
                    updatedAt: '2026-07-29T09:14:45.622Z'
                succeeded:
                  summary: Done, with a receipt
                  value:
                    id: 1f2e3d4c-5b6a-4790-8123-abcdef012345
                    status: succeeded
                    target:
                      kind: direct_url
                      url: https://your-sandbox-store.myshopify.com/products/widget
                      request: buy this, cheapest delivery
                    constraints: {}
                    metadata: {}
                    progressItems:
                      - kind: terminal
                        label: Checkout succeeded
                        data: {}
                        at: '2026-07-29T09:26:29.688Z'
                        sequence: 17
                    receipt:
                      total:
                        amount: '42.10'
                        currency: USD
                      merchantOrderId: '1042'
                      evidence:
                        url: >-
                          https://your-sandbox-store.myshopify.com/checkouts/c/abc123/thank_you
                    createdAt: '2026-07-29T09:14:31.190Z'
                    updatedAt: '2026-07-29T09:26:29.702Z'
                failed:
                  summary: Stopped by the maxCost guardrail
                  value:
                    id: 1f2e3d4c-5b6a-4790-8123-abcdef012345
                    status: failed
                    target:
                      kind: direct_url
                      url: https://your-sandbox-store.myshopify.com/products/widget
                    constraints:
                      maxCost:
                        amount: '60.00'
                        currency: USD
                    metadata: {}
                    progressItems:
                      - kind: terminal
                        label: Checkout failed
                        data:
                          reason: max_cost_exceeded
                          message: Order total 61.40 USD exceeds maxCost 60.00 USD
                        at: '2026-07-29T09:25:10.114Z'
                        sequence: 12
                    failure:
                      reason: max_cost_exceeded
                    createdAt: '2026-07-29T09:14:31.190Z'
                    updatedAt: '2026-07-29T09:25:10.220Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/CheckoutNotFound'
components:
  parameters:
    CheckoutId:
      name: id
      in: path
      required: true
      description: The checkout id returned at creation.
      schema:
        type: string
        format: uuid
  schemas:
    Checkout:
      type: object
      title: Checkout
      description: The whole state of one run. This is what you poll.
      required:
        - id
        - status
        - target
        - constraints
        - metadata
        - progressItems
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/CheckoutStatus'
        subject:
          type: string
          description: >-
            Your own id for the end user this purchase is for. **Absent** when
            the checkout is not tagged to one. Set explicitly at creation, or
            stamped from the authenticating key's binding.
          examples:
            - user_a91f
        target:
          $ref: '#/components/schemas/CheckoutTarget'
        constraints:
          $ref: '#/components/schemas/CheckoutConstraints'
        buyerProfileId:
          type: string
          format: uuid
          description: >-
            Absent when no profile was attached, and absent after the referenced
            profile was deleted.
        metadata:
          type: object
          additionalProperties: true
          description: >-
            Yours. Stored and echoed back untouched; never sent to a merchant,
            and not searchable.
        browser:
          type: object
          description: >-
            Present once a browser session exists. Not a URL you can watch at —
            a pointer to the endpoint that mints one.
          properties:
            embedTokenUrl:
              type: string
              description: '`POST` here to mint a live-view token.'
              examples:
                - /v1/checkouts/1f2e3d4c-5b6a-4790-8123-abcdef012345/embed-token
            permissions:
              type: array
              items:
                type: string
              examples:
                - - read
        progressItems:
          type: array
          items:
            $ref: '#/components/schemas/ProgressItem'
          description: >-
            Append-only, ordered by `sequence`. Build your UI around this rather
            than a spinner.
        pendingUserAction:
          $ref: '#/components/schemas/PendingUserAction'
        failure:
          type: object
          required:
            - reason
          properties:
            reason:
              $ref: '#/components/schemas/FailureReason'
        receipt:
          $ref: '#/components/schemas/Receipt'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CheckoutStatus:
      type: string
      title: CheckoutStatus
      description: '`succeeded`, `failed` and `cancelled` are terminal; nothing leaves them.'
      enum:
        - queued
        - running
        - awaiting_user_action
        - succeeded
        - failed
        - cancelled
    CheckoutTarget:
      type: object
      title: CheckoutTarget
      required:
        - url
      properties:
        kind:
          type: string
          enum:
            - direct_url
          default: direct_url
          description: >-
            Only one kind exists. There is no product search — you bring the
            URL.
        url:
          type: string
          format: uri
          description: The product or cart page to start from.
        request:
          type: string
          description: >-
            Plain English, for the agent. `"buy this in medium, cheapest
            delivery"`. A request that unambiguously names an option value skips
            the variant question entirely.
        merchantContext:
          type: string
          maxLength: 4000
          description: >-
            Anything the agent should know about this merchant that the page
            will not tell it.
        allowedDomains:
          type: array
          maxItems: 10
          items:
            type: string
            pattern: ^[a-z0-9.-]+$
          description: >-
            Extra hostnames the run may navigate to beyond the target's own
            domain — e.g. a marketplace's separate auth domain. Hostnames only:
            no scheme, no path.


            This set is fixed before the run starts and cannot grow at runtime,
            which is what stops untrusted page text steering a session holding
            buyer PII somewhere else.
        countryCode:
          type: string
          pattern: ^[A-Za-z]{2}$
          description: >-
            ISO 3166-1 alpha-2 for the merchant. Matches network egress to the
            merchant's region so it sees a local visitor. Inferred from the
            URL's ccTLD when omitted.
    CheckoutConstraints:
      type: object
      title: CheckoutConstraints
      properties:
        maxCost:
          allOf:
            - $ref: '#/components/schemas/Money'
          description: >-
            Spending ceiling for this checkout, checked before a card is ever
            requested. Omitting it means no cost enforcement at all.


            A strong guardrail, not a hard ceiling: the order-total check
            depends on reading a total off the merchant's page, and if none can
            be parsed there is nothing to compare. The real ceiling should be
            the card.
    ProgressItem:
      type: object
      title: ProgressItem
      description: One entry on the append-only timeline.
      required:
        - kind
        - label
        - data
        - at
        - sequence
      properties:
        kind:
          type: string
          description: >-
            `intent_started`, `step`, `user_action`, `terminal`, and others as
            the engines grow. Treat unknown kinds as informational.
          examples:
            - step
        label:
          type: string
          description: Written for a person. Render as-is.
        data:
          type: object
          additionalProperties: true
          description: >-
            Structured detail for this entry. `user_action` items carry `key`;
            the final `terminal` item on a failure carries `reason` and
            `message`.
        at:
          type: string
          format: date-time
        sequence:
          type: integer
          description: >-
            Per-checkout, monotonic from 1. Track the highest you have seen to
            stream only what is new.
    PendingUserAction:
      type: object
      title: PendingUserAction
      description: >-
        Present only while the run is blocked. At most one per checkout at a
        time.
      required:
        - id
        - message
        - responseSchema
        - expiresAt
      properties:
        id:
          type: string
          format: uuid
          description: >-
            Goes in the submit URL. A new action gets a new id every time —
            never cache one.
        key:
          type: string
          description: >-
            What is being asked for. Branch on this, not on the shape of
            `responseSchema`.


            The known set: `variant` (which size/colour — answerable by an
            agent), `shipping` (a delivery address — answerable if your app
            holds one), `card`, `credentials`, `otp` (all three come from the
            human, never from an agent's context). Tier 2 can raise a short slug
            of its own for something unanticipated; treat unknown keys as "route
            this to a person".


            Absent on actions raised before this field existed.
          examples:
            - variant
        message:
          type: string
          description: >-
            Written for a person, in the context of this specific page. Render
            it as-is.
        responseSchema:
          type: object
          additionalProperties: true
          description: >-
            A real JSON Schema for the object you must send back as `values`.
            Always `{"type":"object","additionalProperties":false,…}` with a
            `required` array and a `properties` map; every property is `type:
            "string"`.
        expiresAt:
          type: string
          format: date-time
          description: >-
            Authoritative. Default TTL is 10 minutes. Past this instant,
            answering fails and the checkout fails with `user_action_expired`.
        detectedTotal:
          allOf:
            - $ref: '#/components/schemas/Money'
          description: >-
            The merchant's own order total, shipping and tax included, read off
            the live page when the run stopped to ask. Present on card actions;
            best-effort, and currency may be absent. Show it to the user before
            answering — receipt.total only exists after the money has moved.
    FailureReason:
      type: string
      title: FailureReason
      description: >-
        A closed set of four. The human-readable detail lives in the final
        `terminal` progress item's `data.message`, not here.
      enum:
        - max_cost_exceeded
        - user_cancelled
        - user_action_expired
        - automation_failed
    Receipt:
      type: object
      title: Receipt
      description: Present only on a `succeeded` checkout.
      properties:
        total:
          $ref: '#/components/schemas/Money'
        merchantOrderId:
          type:
            - string
            - 'null'
          description: The merchant's own format. Do not parse it.
        evidence:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            What the agent saw at confirmation — typically `{ "url":
            "…/thank_you" }`.
    Error:
      type: object
      title: Error
      description: >-
        Every error is this object. There are no error codes: match on the HTTP
        status, treat `message` as text for a human.
      required:
        - error
        - message
      properties:
        error:
          type: boolean
          const: true
        message:
          type: string
    ScopeError:
      type: object
      title: ScopeError
      description: An out-of-scope request. `requiredScope` saves you parsing the message.
      required:
        - error
        - message
        - requiredScope
      properties:
        error:
          type: boolean
          const: true
        message:
          type: string
        requiredScope:
          $ref: '#/components/schemas/Scope'
    Money:
      type: object
      title: Money
      required:
        - amount
      properties:
        amount:
          type: string
          description: A decimal string — `"42.10"`, never `42.1`.
          examples:
            - '42.10'
        currency:
          type: string
          default: USD
          description: ISO 4217.
          examples:
            - USD
    Scope:
      type: string
      title: Scope
      description: >-
        `<resource>.read` or `<resource>.write`, or the wildcard `*`. `write`
        implies `read` on the same resource. Resources are exactly the route
        groups under `/v1`.
      enum:
        - '*'
        - checkouts.read
        - checkouts.write
        - buyer-profiles.read
        - buyer-profiles.write
        - buyer-sessions.read
        - buyer-sessions.write
        - webhooks.read
        - webhooks.write
        - api-keys.read
        - api-keys.write
        - usage.read
        - usage.write
  responses:
    Unauthorized:
      description: >-
        Missing, malformed, invalid or revoked bearer token. There is no
        distinction between "never existed" and "revoked" — that difference is
        only useful to someone probing.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing:
              value:
                error: true
                message: Missing bearer token
            invalid:
              value:
                error: true
                message: Invalid or revoked API key
    Forbidden:
      description: >-
        The key is missing the scope this route requires, the tenant is
        suspended, or the key is bound to one end user and this route answers an
        account-wide question.
      content:
        application/json:
          schema:
            anyOf:
              - $ref: '#/components/schemas/ScopeError'
              - $ref: '#/components/schemas/Error'
          examples:
            scope:
              value:
                error: true
                message: This API key is missing the 'buyer-profiles.read' scope.
                requiredScope: buyer-profiles.read
            suspended:
              value:
                error: true
                message: Tenant suspended
            subjectBound:
              summary: A subject-bound key reaching for a tenant-wide fact
              value:
                error: true
                message: A subject-bound key cannot read account usage.
    CheckoutNotFound:
      description: >-
        No such checkout, or it belongs to another tenant — the two are not
        distinguished.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: true
            message: Checkout not found
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        `Authorization: Bearer ck_test_…` or `ck_live_…`. There is no other auth
        scheme — no query-string keys, no request signing, no OAuth.

````