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

# Charge a saved card

> Charge the card the caller chose against an open Checkout Session. A decline is a 402 in the standard error envelope, not a 200 result.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/charge
openapi: 3.1.0
info:
  title: Ringup API
  version: v1
  description: >-
    Take a card payment on a phone call. Every endpoint is authenticated with a
    Bearer API key; the key prefix (`rk_test_` / `rk_live_`) selects the
    environment.
servers:
  - url: https://api.ringup.dev
security: []
paths:
  /v1/charge:
    post:
      summary: Charge a saved card
      description: >-
        Charge the card the caller chose against an open Checkout Session. A
        decline is a 402 in the standard error envelope, not a 200 result.
      operationId: charge
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChargeRequest'
            example:
              checkout_session_id: cs_test_453b5401286d0f80
              instrument_id: ins_9d21c7
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChargeResponse'
              example:
                status: succeeded
                amount_cents: 1650
                currency: USD
                replay: false
                charge_id: chg_77386e2f
                checkout_session_id: cs_test_453b5401286d0f80
                processor_payment_id: pay_9J2mQ7bK4dR
                merchant_id: mch_4f2c9a1b7e3d
                order_id: 8QK4M2XJ
                platform_call_id: call_7c2a91e4
        '400':
          description: >-
            `invalid_json` The request body is not valid JSON.


            `missing_required_fields` A required parameter is missing.


            `invalid_value` A parameter was present but its value is not
            acceptable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                invalid_json:
                  summary: The request body is not valid JSON.
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_json
                      audience: developer
                      message: The request body is not valid JSON.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#invalid_json
                      request_id: req_8f3c1d2ab4e9
                missing_required_fields:
                  summary: A required parameter is missing.
                  value:
                    error:
                      type: invalid_request_error
                      code: missing_required_fields
                      audience: developer
                      message: A required parameter is missing.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#missing_required_fields
                      request_id: req_8f3c1d2ab4e9
                invalid_value:
                  summary: A parameter was present but its value is not acceptable.
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_value
                      audience: developer
                      message: A parameter was present but its value is not acceptable.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#invalid_value
                      request_id: req_8f3c1d2ab4e9
        '401':
          description: >-
            `invalid_key` The API key is missing, malformed, revoked, or does
            not match the mode of the resource.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                invalid_key:
                  summary: >-
                    The API key is missing, malformed, revoked, or does not
                    match the mode of the resource.
                  value:
                    error:
                      type: authentication_error
                      code: invalid_key
                      audience: developer
                      message: >-
                        The API key is missing, malformed, revoked, or does not
                        match the mode of the resource.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#invalid_key
                      request_id: req_8f3c1d2ab4e9
        '402':
          description: >-
            `card_declined` The issuer declined the charge. `decline_code`
            carries the specific reason where the issuer gave one.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                card_declined:
                  summary: >-
                    The issuer declined the charge. `decline_code` carries the
                    specific reason where the issuer gave one.
                  value:
                    error:
                      type: card_error
                      code: card_declined
                      audience: caller
                      message: >-
                        The issuer declined the charge. `decline_code` carries
                        the specific reason where the issuer gave one.
                      decline_code: insufficient_funds
                      advice_code: try_again_later
                      agent_message: >-
                        That card was declined for insufficient funds. Would you
                        like to try a different card?
                      display_message: >-
                        That card was declined for insufficient funds. Try
                        another card.
                      agent_action: offer_different_card
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#card_declined
                      charge: chg_77386e2f
                      request_id: req_8f3c1d2ab4e9
        '403':
          description: >-
            `instrument_not_authorized` That saved card does not belong to the
            caller on this session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                instrument_not_authorized:
                  summary: >-
                    That saved card does not belong to the caller on this
                    session.
                  value:
                    error:
                      type: permission_error
                      code: instrument_not_authorized
                      audience: developer
                      message: >-
                        That saved card does not belong to the caller on this
                        session.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#instrument_not_authorized
                      request_id: req_8f3c1d2ab4e9
        '404':
          description: >-
            `session_not_found` No checkout session with that id belongs to your
            account.


            `instrument_not_found` No saved card with that id belongs to this
            caller.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                session_not_found:
                  summary: No checkout session with that id belongs to your account.
                  value:
                    error:
                      type: invalid_request_error
                      code: session_not_found
                      audience: developer
                      message: >-
                        No checkout session with that id belongs to your
                        account.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#session_not_found
                      request_id: req_8f3c1d2ab4e9
                instrument_not_found:
                  summary: No saved card with that id belongs to this caller.
                  value:
                    error:
                      type: invalid_request_error
                      code: instrument_not_found
                      audience: caller
                      message: No saved card with that id belongs to this caller.
                      advice_code: do_not_try_again
                      agent_message: >-
                        That card could not be processed. Would you like to try
                        a different card?
                      display_message: >-
                        That card could not be processed. Would you like to try
                        a different card?
                      agent_action: offer_different_card
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#instrument_not_found
                      request_id: req_8f3c1d2ab4e9
        '409':
          description: >-
            `processor_mismatch` That saved card cannot be charged at this
            merchant, because it lives in a different processor's wallet.


            `charge_details_mismatch` This idempotency key was already used with
            different charge details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                processor_mismatch:
                  summary: >-
                    That saved card cannot be charged at this merchant, because
                    it lives in a different processor's wallet.
                  value:
                    error:
                      type: invalid_request_error
                      code: processor_mismatch
                      audience: caller
                      message: >-
                        That saved card cannot be charged at this merchant,
                        because it lives in a different processor's wallet.
                      advice_code: do_not_try_again
                      agent_message: >-
                        I can text you a secure link to pay instead. Would that
                        work?
                      display_message: >-
                        I can text you a secure link to pay instead. Would that
                        work?
                      agent_action: send_link
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#processor_mismatch
                      request_id: req_8f3c1d2ab4e9
                charge_details_mismatch:
                  summary: >-
                    This idempotency key was already used with different charge
                    details.
                  value:
                    error:
                      type: idempotency_error
                      code: charge_details_mismatch
                      audience: developer
                      message: >-
                        This idempotency key was already used with different
                        charge details.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#charge_details_mismatch
                      request_id: req_8f3c1d2ab4e9
        '410':
          description: >-
            `session_expired` The checkout session passed its expiry and can no
            longer be paid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                session_expired:
                  summary: >-
                    The checkout session passed its expiry and can no longer be
                    paid.
                  value:
                    error:
                      type: invalid_request_error
                      code: session_expired
                      audience: developer
                      message: >-
                        The checkout session passed its expiry and can no longer
                        be paid.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#session_expired
                      request_id: req_8f3c1d2ab4e9
        '422':
          description: >-
            `session_missing_consumer` The session has no caller identity
            attached, so no card can be selected.


            `merchant_not_connected` The merchant exists but has not finished
            connecting their payment processor, so no charge can settle.


            `merchant_credentials_invalid` The merchant's stored processor
            credentials were rejected, so nothing can settle.


            `needs_enrollment` The caller has no card on file usable at this
            merchant, so there is nothing to charge. Expected for a first-time
            caller at a per-merchant processor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                session_missing_consumer:
                  summary: >-
                    The session has no caller identity attached, so no card can
                    be selected.
                  value:
                    error:
                      type: invalid_request_error
                      code: session_missing_consumer
                      audience: developer
                      message: >-
                        The session has no caller identity attached, so no card
                        can be selected.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#session_missing_consumer
                      request_id: req_8f3c1d2ab4e9
                merchant_not_connected:
                  summary: >-
                    The merchant exists but has not finished connecting their
                    payment processor, so no charge can settle.
                  value:
                    error:
                      type: invalid_request_error
                      code: merchant_not_connected
                      audience: developer
                      message: >-
                        The merchant exists but has not finished connecting
                        their payment processor, so no charge can settle.
                      advice_code: do_not_try_again
                      agent_message: null
                      display_message: null
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#merchant_not_connected
                      request_id: req_8f3c1d2ab4e9
                merchant_credentials_invalid:
                  summary: >-
                    The merchant's stored processor credentials were rejected,
                    so nothing can settle.
                  value:
                    error:
                      type: processor_error
                      code: merchant_credentials_invalid
                      audience: caller
                      message: >-
                        The merchant's stored processor credentials were
                        rejected, so nothing can settle.
                      advice_code: do_not_try_again
                      agent_message: I'm not able to take payment on this line right now.
                      display_message: This business cannot take payment right now.
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#merchant_credentials_invalid
                      request_id: req_8f3c1d2ab4e9
                needs_enrollment:
                  summary: >-
                    The caller has no card on file usable at this merchant, so
                    there is nothing to charge. Expected for a first-time caller
                    at a per-merchant processor.
                  value:
                    error:
                      type: invalid_request_error
                      code: needs_enrollment
                      audience: caller
                      message: >-
                        The caller has no card on file usable at this merchant,
                        so there is nothing to charge. Expected for a first-time
                        caller at a per-merchant processor.
                      advice_code: do_not_try_again
                      agent_message: >-
                        I can text you a secure link to pay instead. Would that
                        work?
                      display_message: >-
                        I can text you a secure link to pay instead. Would that
                        work?
                      agent_action: send_link
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#needs_enrollment
                      request_id: req_8f3c1d2ab4e9
        '424':
          description: >-
            `processor_unavailable` The merchant's payment processor is
            unreachable or returned a server error, so the outcome of this
            charge is UNKNOWN. It may still have settled. Not a Ringup outage.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                processor_unavailable:
                  summary: >-
                    The merchant's payment processor is unreachable or returned
                    a server error, so the outcome of this charge is UNKNOWN. It
                    may still have settled. Not a Ringup outage.
                  value:
                    error:
                      type: processor_error
                      code: processor_unavailable
                      audience: caller
                      message: >-
                        The merchant's payment processor is unreachable or
                        returned a server error, so the outcome of this charge
                        is UNKNOWN. It may still have settled. Not a Ringup
                        outage.
                      advice_code: try_again_later
                      agent_message: >-
                        I'm still confirming that payment. I'll send you a
                        confirmation as soon as it comes through.
                      display_message: >-
                        We are still confirming this payment. Do not pay again:
                        we will send a confirmation as soon as it comes through.
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#processor_unavailable
                      request_id: req_8f3c1d2ab4e9
        '429':
          description: '`rate_limit` Too many requests in a short window.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorBody'
              examples:
                rate_limit:
                  summary: Too many requests in a short window.
                  value:
                    error:
                      type: rate_limit_error
                      code: rate_limit
                      audience: caller
                      message: Too many requests in a short window.
                      advice_code: try_again_later
                      agent_message: >-
                        I'm having trouble taking payment right now. Let me get
                        someone to help you.
                      display_message: >-
                        Too many attempts just now. Please wait a moment and try
                        again.
                      agent_action: null
                      doc_url: >-
                        https://docs.ringup.dev/api-reference/error-codes#rate_limit
                      request_id: req_8f3c1d2ab4e9
      security:
        - bearerAuth: []
components:
  schemas:
    ChargeRequest:
      additionalProperties: false
      properties:
        checkout_session_id:
          type: string
        instrument_id:
          description: >-
            Required, never defaulted. The caller verbally agreed to a SPECIFIC
            card, so an implicit default is how a misheard "use my other one"
            charges the wrong one.
          type: string
      required:
        - checkout_session_id
        - instrument_id
      type: object
    ChargeResponse:
      additionalProperties: false
      properties:
        amount_cents:
          type: number
        charge_id:
          type: string
        checkout_session_id:
          type: string
        currency:
          type: string
        merchant_id:
          type: string
        order_id:
          description: >-
            Also the confirmation number the agent reads back: it is the
            MERCHANT's, not ours.
          type:
            - string
            - 'null'
        platform_call_id:
          type:
            - string
            - 'null'
        processor_payment_id:
          type:
            - string
            - 'null'
        replay:
          description: >-
            True when this exact charge already happened and you are seeing the
            original.
          type: boolean
        status:
          $ref: '#/components/schemas/ChargeStatus'
        wallet:
          description: >-
            Which wallet paid, when the payment came from the pay page's Apple
            Pay or Google Pay button. Absent on card charges. Wallet payments
            never create a saved card (the processor does not allow a wallet to
            become a card on file), so a wallet charge references no saved
            instrument.
          enum:
            - apple_pay
            - google_pay
          type: string
      type: object
    ApiErrorBody:
      additionalProperties: false
      description: >-
        THE error object. Identical on every endpoint: provisioning, identify,
        checkout, cards, charge. A failed charge (which arrives as a 200 with
        `status: "failed"`) carries the SAME fields under `failure`, so a
        developer writes one parser and it works everywhere.
      properties:
        error:
          additionalProperties: false
          properties:
            advice_code:
              $ref: '#/components/schemas/AdviceCode'
              description: try_again_later | confirm_card_data | do_not_try_again
            agent_action:
              anyOf:
                - $ref: '#/components/schemas/AgentAction'
                - type: 'null'
              description: 'Null on every `developer`-audience error: see agent_message.'
            agent_message:
              description: >-
                Speak verbatim WHEN NON-NULL. Null on every `developer`-audience
                error.
              type:
                - string
                - 'null'
            audience:
              $ref: '#/components/schemas/Audience'
              description: >-
                Who this error is for. Drives whether there is anything to speak
                at all.
            charge:
              description: Present when the error concerns a specific charge.
              type: string
            code:
              $ref: '#/components/schemas/ErrorCode'
            decline_code:
              type: string
            display_message:
              description: >-
                The same fact written for a screen. Null on `developer`-audience
                errors.
              type:
                - string
                - 'null'
            doc_url:
              type: string
            message:
              type: string
            param:
              type: string
            processor_code:
              type: string
            request_id:
              type: string
            request_log_url:
              description: >-
                Deep link to this request in the dashboard. Present once the
                dashboard log view exists.
              type: string
            type:
              $ref: '#/components/schemas/ErrorType'
          type: object
      type: object
    ChargeStatus:
      description: >-
        Where a charge stands.


        `pending` means the charge is not settled: the processor has not
        confirmed an outcome, and it may or may not have taken the payment. Do
        NOT start another payment for the same order. We resolve it for you and
        the result arrives on the webhook.


        `completed` and `failed` are settled and final.
      enum:
        - pending
        - succeeded
        - failed
      type: string
    AdviceCode:
      description: >-
        `advice_code` is our guidance to the developer on what to do with this
        decline programmatically. It is distinct from `agent_action`, which
        drives the conversation. A decline can be "do not retry this card"
        (advice) while still being "offer a different card" (agent action).
      enum:
        - try_again_later
        - do_not_try_again
        - confirm_card_data
      type: string
    AgentAction:
      description: >-
        What the agent should DO next. Keeps conversational branching
        deterministic too.
      enum:
        - retry_card
        - offer_different_card
        - send_link
        - end_call
        - escalate
      type: string
    Audience:
      description: >-
        Who an error is for.


        `caller` errors are things the person on the phone is living through, so
        they carry copy your agent can speak. `developer` errors are your
        integration's own bugs, and carry NO speakable copy at all: the fields
        are null rather than a neutral filler, because a filler sentence invites
        an agent to raise payment when nothing about payment has failed.
      enum:
        - developer
        - caller
      type: string
    ErrorCode:
      enum:
        - invalid_json
        - missing_required_fields
        - invalid_value
        - invalid_key
        - vendor_not_provisioned
        - merchant_not_found
        - merchant_not_connected
        - invalid_processor
        - invalid_credentials
        - invalid_payment_policy
        - invalid_payment_policy_amount_cents
        - session_not_found
        - session_expired
        - session_create_failed
        - session_missing_consumer
        - missing_checkout_session_id
        - order_lookup_failed
        - order_lookup_unsupported
        - instrument_not_found
        - instrument_not_authorized
        - missing_instrument_fields
        - processor_mismatch
        - needs_enrollment
        - charge_details_mismatch
        - card_save_failed
        - card_declined
        - processor_unavailable
        - merchant_credentials_invalid
        - invalid_vault
        - key_not_found
        - invalid_environment
        - invalid_url
        - invalid_events
        - invalid_ttl
        - invalid_state_key
        - not_found
        - not_configured
        - rate_limit
        - internal_error
      type: string
    ErrorType:
      enum:
        - invalid_request_error
        - authentication_error
        - permission_error
        - card_error
        - processor_error
        - idempotency_error
        - rate_limit_error
        - api_error
      type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: rk_live_…

````