Agentcard Connect API

Connect a user to your platform: send a one-time code, verify it, record consent, and keep the connection alive.

OpenAPI Specification

agentcard-connect-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Agentcard Authentication Connect API
  version: 2.0.0
  description: The Agentcard v2 API — connect your users and verify their identity from your own backend. Every call is authenticated with a platform access token minted from your `client_id` + `client_secret`.
servers:
- url: https://api.agentcard.sh
  description: There is one base URL. Sandbox vs production is decided by the client credential you use, never by the host.
security:
- platformToken: []
tags:
- name: Connect
  description: 'Connect a user to your platform: send a one-time code, verify it, record consent, and keep the connection alive.'
paths:
  /api/v2/connect/start:
    post:
      tags:
      - Connect
      summary: Send a code
      operationId: connectStart
      description: 'Sends a one-time code to the user by email or phone. Provide **exactly one** of `email` or `phone`. Codes are valid for 10 minutes.


        If you''ve designed a connect email in your dashboard, we send that branded email; otherwise we send a default one.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  description: The user's email address. Provide this or `phone`, not both.
                phone:
                  type: string
                  description: The user's phone number in E.164 format (e.g. `+15551234567`). Provide this or `email`, not both.
                external_user_id:
                  type: string
                  maxLength: 255
                  description: Optional. Your own identifier for the user, stored on the attempt for your reference.
            example:
              email: user@example.com
      responses:
        '201':
          description: The attempt. Pass its `id` back as `connect_id` when you verify.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectAttempt'
              example:
                object: connect_attempt
                id: ca_9f8e7d6c
                channel: email
                expires_at: '2026-07-12T20:30:00.000Z'
        '400':
          description: '`invalid_request` — missing or both of `email` and `phone`. `invalid_phone` — the number isn''t valid or supported. `client_credentials_required` — the token wasn''t minted from client credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '502':
          description: '`auth_provider_error` — the code couldn''t be sent. Try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: '`auth_unavailable` — authentication is temporarily unavailable. Retry shortly.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/connect/verify:
    post:
      tags:
      - Connect
      summary: Verify the code
      operationId: connectVerify
      description: 'Checks the code the user entered and, on success, connects the user and returns the token pair to store. Completing the code **is** the authorization — there is no separate approval screen.


        The returned `access_token` is the **user''s connection token**: it acts on behalf of this user (send it as the bearer token to the MCP server to create cards, check balances, and shop as them). It is not the platform token — the endpoints in this reference keep using your platform access token and name the user with `user_id`.


        A code can be verified once: a second verify of the same attempt returns `invalid_connect_attempt`.


        In **sandbox** the code is always `111111`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - connect_id
              - code
              properties:
                connect_id:
                  type: string
                  description: The `id` returned by `/connect/start`.
                code:
                  type: string
                  maxLength: 12
                  description: The one-time code the user entered. Always `111111` in sandbox.
            example:
              connect_id: ca_9f8e7d6c
              code: '111111'
      responses:
        '200':
          description: The connection. Store the token pair and `user.id` — every KYC call names the user by it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Connection'
              example:
                object: connection
                access_token: act_1a2b3c…
                refresh_token: rct_4d5e6f…
                token_type: Bearer
                expires_in: 3600
                user:
                  id: user_7g8h9i
                  email: user@example.com
                  phone: null
        '400':
          description: '`invalid_request` — missing `connect_id` or `code`. `invalid_connect_attempt` — the attempt is unknown, already used, or expired. `client_credentials_required` — the token wasn''t minted from client credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`invalid_code` — that code is invalid or expired.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: '`auth_provider_error` — verification failed downstream. Try again.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/connect/consent:
    post:
      tags:
      - Connect
      summary: Record consent
      operationId: connectConsent
      description: Records that the user authorized your platform to act on their behalf. Safe to retry — it's idempotent per user, so a repeat call updates the same record instead of creating a duplicate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - user_id
              properties:
                user_id:
                  type: string
                  description: The `user.id` from `/connect/verify`.
                terms_version:
                  type: string
                  maxLength: 64
                  description: Optional. The version of your terms the user accepted, stored for your audit trail.
            example:
              user_id: user_7g8h9i
              terms_version: '2026-07-01'
      responses:
        '201':
          description: The consent record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Consent'
              example:
                object: consent
                id: cns_1a2b3c
                user_id: user_7g8h9i
                terms_version: '2026-07-01'
                created_at: '2026-07-12T20:30:00.000Z'
        '400':
          description: '`invalid_request` — missing `user_id`. `client_credentials_required` — the token wasn''t minted from client credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: '`connection_not_found` — no connection exists for that user under your client.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v2/connect/refresh:
    post:
      tags:
      - Connect
      summary: Refresh the connection
      operationId: connectRefresh
      description: 'Connection access tokens expire after one hour. Exchange the refresh token for a new pair before then.


        Each refresh returns a **new** refresh token and invalidates the old one — replace the stored token every time.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - refresh_token
              properties:
                refresh_token:
                  type: string
                  description: The refresh token from the most recent `/connect/verify` or `/connect/refresh`.
            example:
              refresh_token: rct_4d5e6f…
      responses:
        '200':
          description: The new token pair.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectionRefreshed'
              example:
                object: connection
                access_token: act_9z8y7x…
                refresh_token: rct_6w5v4u…
                token_type: Bearer
                expires_in: 3600
        '400':
          description: '`invalid_request` — missing `refresh_token`. `client_credentials_required` — the token wasn''t minted from client credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`invalid_refresh_token` — that refresh token is invalid or expired.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    ConnectionRefreshed:
      type: object
      properties:
        object:
          type: string
          enum:
          - connection
        access_token:
          type: string
          description: The new connection access token.
        refresh_token:
          type: string
          description: The new refresh token. The old one is now invalid — replace the stored token every time.
        token_type:
          type: string
          enum:
          - Bearer
        expires_in:
          type: integer
          description: Seconds until the access token expires (3600 = one hour).
    Error:
      type: object
      description: Every v2 error uses the same envelope.
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: A stable, machine-readable string (snake_case). Branch on this.
            message:
              type: string
              description: A human-readable explanation, safe to log.
            docs:
              type: string
              description: A link back to the reference.
            field_errors:
              type: object
              additionalProperties:
                type: string
              description: Only on `invalid_fields` — names each field to fix.
            warnings:
              type: array
              items:
                type: string
              description: Only on document upload errors — actionable feedback safe to show the user.
    Connection:
      type: object
      properties:
        object:
          type: string
          enum:
          - connection
        access_token:
          type: string
          description: The user's connection token — store it to act on their behalf. Send it as the bearer token to the MCP server; never in the `Authorization` header of these endpoints.
        refresh_token:
          type: string
          description: Use it with `/connect/refresh` to get a new pair before the access token expires.
        token_type:
          type: string
          enum:
          - Bearer
        expires_in:
          type: integer
          description: Seconds until the access token expires (3600 = one hour).
        user:
          type: object
          description: The connected user. Store `id` — every KYC call names the user by it.
          properties:
            id:
              type: string
            email:
              type:
              - string
              - 'null'
              description: Set when the user connected by email, otherwise `null`.
            phone:
              type:
              - string
              - 'null'
              description: Set when the user connected by phone, otherwise `null`.
    Consent:
      type: object
      properties:
        object:
          type: string
          enum:
          - consent
        id:
          type: string
        user_id:
          type: string
        terms_version:
          type:
          - string
          - 'null'
        created_at:
          type: string
          format: date-time
    ConnectAttempt:
      type: object
      properties:
        object:
          type: string
          enum:
          - connect_attempt
        id:
          type: string
          description: The attempt id. Pass it back as `connect_id` when you verify.
        channel:
          type: string
          enum:
          - email
          - phone
          description: The channel the code was sent on.
        expires_at:
          type: string
          format: date-time
          description: When the attempt expires (ISO 8601). Codes are valid for 10 minutes.
  responses:
    Unauthorized:
      description: '`unauthorized` — the platform access token is missing or expired. Exchange your client credentials for a fresh one.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    platformToken:
      type: http
      scheme: bearer
      description: 'A platform access token. Get one on the **Create an access token** endpoint by exchanging your `client_id` + `client_secret`, then send it as `Authorization: Bearer <token>`. Tokens live one hour.'