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

# Where the payment provider delivers

> NOT AN ENDPOINT YOU CALL. The payment provider posts here, and this is the step that turns a settled payment into admission and a provisioned device, and a reversal back out again.

NO CREDENTIAL OF OURS AUTHENTICATES IT, because the sender holds none. A delivery is authenticated by a signature over the raw body, checked before the body is parsed and before anything is recorded. No `Authorization` header is read here and sending one changes nothing, which is why this route sits outside `/v1` rather than inside it.

WHAT THE STATUS CODES MEAN, which is the real contract with the sender. 200 is settled: the transaction that recorded the event committed before the answer was sent, so the delivery is finished. 400 is a delivery that was not authentic or not readable, and nothing was recorded. 5xx is ours to fix and the delivery should be repeated; there is deliberately no retry inside this process. A repeat of an event already recorded settles again rather than applying twice.

A body over 262144 bytes is refused as it streams, before any of it is held in memory. That is tighter than the service wide cap the 413 below describes, and it is the one that applies here.

IT IS ABSENT ON A DEPLOYMENT THAT HAS NO SIGNING SECRET CONFIGURED. An endpoint that accepts bodies it cannot authenticate is worse than a 404, so such a deployment does not mount it at all.

Hosted deployments only. A local checkout does not mount this route, so calling it there is a 404.



## OpenAPI

````yaml /api/openapi.json post /webhooks/stripe
openapi: 3.1.0
info:
  title: Phonebase HTTP API
  version: 0.1.0
  summary: >-
    Every HTTP operation the control plane mounts, generated from the app's own
    route table.
  description: >-
    The control plane exposes two HTTP surfaces, and reading the boundary
    between them matters more than reading any single endpoint.


    The CONTROL surface is what your backend and your operators call. It takes a
    bearer token in the `Authorization` header, and two credential channels
    resolve to the same authorization context: an OAuth 2.1 access token for
    interactive clients, or an org scoped API key for headless ones.


    The DEVICE surface, under `/vrx/{runToken}/v1`, is what a worker calls while
    executing one run. Its credential is the run token in the url path, and it
    reads no `Authorization` header at all.


    Device control itself is not REST. An agent taps, swipes and reads the
    screen over the Model Context Protocol at `POST /mcp`, one tool call at a
    time. There is no `POST /v1/devices/{id}/tap`, and if you are looking for
    one, start with the tool reference instead.


    Operations marked `"x-phonebase-modes": ["hosted"]` exist only on a hosted
    deployment; a local checkout does not mount them.
servers:
  - url: '{origin}'
    description: Your control plane origin.
    variables:
      origin:
        default: https://mcp.phonebase.co
        enum:
          - https://mcp.phonebase.co
          - http://127.0.0.1:8788
        description: >-
          The hosted deployment serves the public origin its metadata document
          advertises, and that is the default here. Select the loopback entry
          instead when you are running a local checkout, which serves the port
          `PORT` names.
security: []
tags:
  - name: Apps
    description: Save immutable application builds and manage explicit device operations.
  - name: Managed Proxy
    description: >-
      Organization-owned fixed egress resources, independent one-time orders and
      verified network relationships.
  - name: Health
    description: Liveness, unauthenticated.
  - name: Gateway enrollment
    description: >-
      How a new gateway computer trades a one-time enrollment code for its
      tunnel credential. Unauthenticated: the code is the credential.
  - name: Gateway releases
    description: >-
      What a gateway computer installs: the current edge release of a channel.
      Called with the gateway's own credential.
  - name: MCP
    description: The JSON-RPC endpoint an agent drives devices through.
  - name: Copilot
    description: >-
      Drive a device by chatting. A signed in person's own conversations,
      streamed. Hosted deployments only.
  - name: Discovery
    description: Where to authenticate. Served on hosted deployments only.
  - name: Runs
    description: Start, watch and steer a run.
  - name: API keys
    description: Mint and revoke headless credentials. Hosted deployments only.
  - name: Device surface
    description: What a worker calls with its run token to observe and act.
  - name: Usage
    description: Read your org's own receipts and consumption. Hosted deployments only.
  - name: Devices
    description: Read the fleet your credential can see. Hosted deployments only.
  - name: Notifications
    description: >-
      What a person needs to know when they are not watching, and which emails
      they receive. Hosted deployments only.
  - name: Webhooks
    description: >-
      Subscribe your own system to what happens here, and read what was
      delivered. Hosted deployments only.
  - name: Provisioning
    description: >-
      Whether an organisation is provisioned, and how it becomes so. Hosted
      deployments only, and both operations answer a declared 503 on a
      deployment that cannot provision yet.
paths:
  /webhooks/stripe:
    post:
      tags:
        - Provisioning
      summary: Where the payment provider delivers
      description: >-
        NOT AN ENDPOINT YOU CALL. The payment provider posts here, and this is
        the step that turns a settled payment into admission and a provisioned
        device, and a reversal back out again.


        NO CREDENTIAL OF OURS AUTHENTICATES IT, because the sender holds none. A
        delivery is authenticated by a signature over the raw body, checked
        before the body is parsed and before anything is recorded. No
        `Authorization` header is read here and sending one changes nothing,
        which is why this route sits outside `/v1` rather than inside it.


        WHAT THE STATUS CODES MEAN, which is the real contract with the sender.
        200 is settled: the transaction that recorded the event committed before
        the answer was sent, so the delivery is finished. 400 is a delivery that
        was not authentic or not readable, and nothing was recorded. 5xx is ours
        to fix and the delivery should be repeated; there is deliberately no
        retry inside this process. A repeat of an event already recorded settles
        again rather than applying twice.


        A body over 262144 bytes is refused as it streams, before any of it is
        held in memory. That is tighter than the service wide cap the 413 below
        describes, and it is the one that applies here.


        IT IS ABSENT ON A DEPLOYMENT THAT HAS NO SIGNING SECRET CONFIGURED. An
        endpoint that accepts bodies it cannot authenticate is worse than a 404,
        so such a deployment does not mount it at all.


        Hosted deployments only. A local checkout does not mount this route, so
        calling it there is a 404.
      operationId: postWebhooksStripe
      requestBody:
        description: >-
          The provider's own event envelope, byte for byte as it sent it. Read
          only once the signature matches.
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  description: >-
                    The provider's id for this event. The key the delivery is
                    recorded under, and what makes a repeat recognisable.
                type:
                  type: string
                  description: >-
                    The provider's event type. A type this service does not act
                    on is recorded and settled, and no field is read off its
                    object.
                created:
                  type: integer
                  description: >-
                    Seconds since the epoch, as the provider stamped it. The
                    only ordering input used, so it is never defaulted to our
                    own clock: an envelope without it is refused rather than
                    guessed at.
                data:
                  type: object
                  properties:
                    object:
                      type: object
                      additionalProperties: true
                      description: The object the event is about.
                  required: []
                  additionalProperties: true
              required:
                - id
                - type
                - created
              additionalProperties: true
              description: >-
                The provider defines this shape. Fields beyond the ones named
                here travel through and are ignored.
      responses:
        '200':
          description: >-
            Settled, so the delivery is finished. Also the answer for an event
            this service does not act on, for a repeat of one already recorded,
            and for a reversal that can never be attributed to an organisation:
            repeating that one cannot add a reference the object does not carry,
            so it raises an operator alarm here instead of being handed back for
            redelivery.
          content:
            application/json:
              schema:
                type: object
                properties:
                  received:
                    type: boolean
                    const: true
                    description: Always true on this response.
                  outcome:
                    type: string
                    description: >-
                      One word naming what was done with the event, recorded so
                      an operator can slice a day of deliveries. Not a field to
                      branch on: the sender does not read it.
                required:
                  - received
                  - outcome
                additionalProperties: false
        '400':
          description: >-
            `invalid_signature` when the delivery is not authentic,
            `invalid_event` when it is authentic but not readable as an event.
            Neither records anything: an unverified body is not evidence that
            any event exists. Why a signature did not match is kept out of the
            answer on purpose.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - invalid_signature
                          - invalid_event
                        description: Which of the two refusals this was.
                    required:
                      - code
                    additionalProperties: false
                required:
                  - error
                additionalProperties: false
        '413':
          description: >-
            The request body exceeds the server's limit (1 MiB). Refused before
            the body is parsed, so nothing was partially applied. Retrying the
            same body will fail the same way: send less. Per-field limits are
            tighter than this and are documented on the fields themselves; this
            is the backstop underneath them. The shape is the same on ordinary
            JSON surfaces, /vrx included. The explicitly configured APK content
            stream documents its own byte ceiling.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PayloadTooLargeError'
        '429':
          description: >-
            Over the request budget for this caller. Hosted deployments only.
            Which budget applied depends on whether this request carried an
            `Authorization` header, not on which endpoint it was aimed at. Wait
            `Retry-After` seconds and retry the same request: the refusal
            happens before any work, so nothing was partially applied. See
            /api/rate-limits.
          headers:
            Retry-After:
              description: >-
                Whole seconds until one request is available again. Computed
                from the caller's own remaining allowance, not a fixed constant.
              schema:
                type: string
            RateLimit-Limit:
              description: >-
                The budget in effect for this caller class, in requests per
                minute.
              schema:
                type: string
            RateLimit-Remaining:
              description: >-
                Whole requests that were still available when this request was
                admitted. Zero on a 429.
              schema:
                type: string
            RateLimit-Reset:
              description: >-
                Seconds until the allowance is back to full. Longer than
                `Retry-After`, which is the wait for a single request.
              schema:
                type: string
            RateLimit-Policy:
              description: The budget and its window, as `<limit>;w=60`.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitedError'
        '500':
          description: >-
            The delivery could not be processed. The transaction rolled back and
            nothing was recorded, so delivering it again is safe and is the
            intended recovery. No body shape is promised on this answer.
        '503':
          description: >-
            `not_settled`: the one outcome left deliberately open, a reversal
            that cannot be attributed YET. Nothing was applied and the delivery
            should be repeated. `Retry-After` says how long to wait.
          headers:
            Retry-After:
              description: Whole seconds to wait before delivering again.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        const: not_settled
                        description: Always `not_settled` on this response.
                    required:
                      - code
                    additionalProperties: false
                required:
                  - error
                additionalProperties: false
      security: []
components:
  schemas:
    PayloadTooLargeError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              const: payload_too_large
              description: Always `payload_too_large` on this response.
            message:
              type: string
              description: A human readable summary naming the limit in bytes.
          required:
            - code
            - message
          additionalProperties: false
      required:
        - error
      additionalProperties: false
      description: >-
        Returned when the request body exceeds the server limit. Identical on
        every surface, including /vrx.
    RateLimitedError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              const: rate_limited
              description: Always `rate_limited` on this response.
            message:
              type: string
              description: >-
                A human readable summary. The numbers are in the headers, not in
                here.
          required:
            - code
            - message
          additionalProperties: false
      required:
        - error
      additionalProperties: false
      description: >-
        Over the request budget. Raised by the rate limiter before any handler
        runs, which is why its code is not one of the action codes: nothing was
        attempted, so no action failed.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.