Agentcard Connect API

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

Operations 5

POST /api/v2/connect/start Send a code #
POST /api/v2/connect/verify Verify the code #
POST /api/v2/connect/consent Record consent #
POST /api/v2/connect/refresh Refresh the connection #
DELETE /api/v2/vault_users/{user_id}/connection Disconnect a user #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/agentcard:agentcard-connect-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

agentcard-connect-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Agentcard 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: usr_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: usr_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: usr_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'
  /api/v2/vault_users/{user_id}/connection:
    delete:
      tags:
      - Connect
      summary: Disconnect a user
      operationId: vaultUserConnectionDelete
      description: 'Ends your app''s connection to a user. Every token your app holds for the user stops working, Vault session links they have not opened stop opening, and their cards stay in their Vault. Idempotent: a second call answers `200` with every count at `0`.'
      parameters:
      - name: user_id
        in: path
        required: true
        description: The user's id, as your app stored it when they connected.
        schema:
          type: string
          maxLength: 64
      responses:
        '200':
          description: The user is disconnected from your app.
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum:
                    - vault_user_connection
                  user_id:
                    type: string
                  connected:
                    type: boolean
                    enum:
                    - false
                  revoked_tokens:
                    type: integer
                    description: Tokens your app held for the user that stopped working on this call.
                  revoked_wallet_links:
                    type: integer
                    description: Wallet links your app sent the user that stopped working on this call.
                  revoked_sessions:
                    type: integer
                    description: Vault session links the user had not opened yet. A disconnected user who taps one gets nothing.
                  test_mode:
                    type: boolean
              example:
                object: vault_user_connection
                user_id: usr_8f3k2m
                connected: false
                revoked_tokens: 2
                revoked_wallet_links: 0
                revoked_sessions: 1
                test_mode: false
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: '`connection_not_found`: your app holds no connection to that user in this mode.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: '`partner_disconnect_pending`: the tokens are off, and the user''s auto-approval for your app is not yet. Call again until it answers `200`.'
          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).
    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.
    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.
  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.'
    userAccessToken:
      type: http
      scheme: bearer
      description: The user's connection access_token (user authentication), or an org-minted buy_token for org-owned accounts.
x-provenance:
  method: harvested
  publisher: Agentcard
  source: https://docs.agentcard.sh/openapi.json
  note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
    stamped by stamp-openapi-provenance.py (roadmap#793)