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

# Top Up Credits

> Add credits via a hosted Stripe Checkout session (**$50 minimum**); not available on annual plans. Redirect the customer to the returned `checkout_session_url`.

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="Top Up Credits Prompt"
  prompt={`Goal: Start a manual credit top-up by creating a hosted Stripe Checkout session. Once Stripe processes the payment, Didit's invoice.paid webhook handler credits the organization's balance.

Endpoint: POST https://verification.didit.me/v3/billing/top-up/
Auth header: x-api-key: <DIDIT_API_KEY>
Content-Type: application/json

Request body:
- amount_in_dollars (number, REQUIRED) — USD amount to charge. MINIMUM $50. Tax is added automatically at checkout based on billing address.
- success_url (URI, optional) — Stripe redirects here after successful payment. Defaults to https://console.didit.me. Append literal placeholder {CHECKOUT_SESSION_ID} if you want Stripe to substitute the session id.
- cancel_url (URI, optional) — Stripe redirects here when the customer aborts. Defaults to https://console.didit.me.

curl:
curl -X POST 'https://verification.didit.me/v3/billing/top-up/' \\
-H 'x-api-key: YOUR_API_KEY' \\
-H 'Content-Type: application/json' \\
-d '{
"amount_in_dollars": 500,
"success_url": "https://yourapp.com/billing/success",
"cancel_url": "https://yourapp.com/billing/cancel"
}'

Response 200:
{
"checkout_session_id": "cs_test_a1B2c3D4e5F6g7H8i9J0",
"checkout_session_url": "https://checkout.stripe.com/c/pay/cs_test_..."
}
Redirect the customer's browser to checkout_session_url (or hand it off to your billing admin out-of-band) to complete payment.

Full flow:
1. POST /v3/billing/top-up/ with amount.
2. Redirect to checkout_session_url.
3. Customer pays on Stripe-hosted page.
4. Stripe redirects to success_url (or cancel_url on abort).
5. Didit receives invoice.paid webhook, increases organization balance.
6. Poll GET /v3/billing/balance/ to confirm balance reflects the top-up.

Failure modes (envelope {"detail": "..."}):
- 400 — common:
{ "detail": "amount_in_dollars is required." }
{ "detail": "Amount must be at least $50." }
{ "detail": "amount_in_dollars must be a number." }
{ "detail": "You have an annual plan. Contact your account manager to top up." }  // ANNUAL-PLAN orgs cannot self-serve top-ups via this endpoint.
- 401 — missing/malformed x-api-key.
- 403 — key valid but cannot create top-up sessions for this organization.
- 429 — rate-limited.

Notes:
- The Stripe Checkout session is single-use, but calling this endpoint repeatedly creates NEW sessions — track completion on your side via success_url/cancel_url to avoid duplicate charges.
- When the cardholder pays with card / SEPA / US ACH, Stripe attaches the payment method with setup_future_usage=off_session so Didit can later auto-charge it for auto-refill.
- The organization must already have a Stripe customer set up (created automatically the first time the org configures billing).

For end-to-end Didit integration, paste in the full prompt at /integration/integration-prompt.`}
/>


## OpenAPI

````yaml POST /v3/billing/top-up/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/billing/top-up/:
    post:
      tags:
        - Billing
      summary: Top up credits
      description: >-
        Add credits via a hosted Stripe Checkout session (**$50 minimum**); not
        available on annual plans. Redirect the customer to the returned
        `checkout_session_url`.
      operationId: billing_top_up
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - amount_in_dollars
              properties:
                amount_in_dollars:
                  type: number
                  minimum: 50
                  description: >-
                    Amount to charge, in USD. **Must be at least 50.** Tax is
                    added automatically at checkout based on the customer's
                    billing address.
                success_url:
                  type: string
                  format: uri
                  description: >-
                    URL Stripe redirects the customer to after a successful
                    payment. Defaults to `https://console.didit.me`. Append the
                    literal placeholder `{CHECKOUT_SESSION_ID}` if you want
                    Stripe to substitute the session ID.
                cancel_url:
                  type: string
                  format: uri
                  description: >-
                    URL Stripe redirects the customer to when they abort or
                    close the Checkout window. Defaults to
                    `https://console.didit.me`.
            examples:
              Minimum top-up:
                value:
                  amount_in_dollars: 50
              Custom amount with redirects:
                value:
                  amount_in_dollars: 500
                  success_url: https://yourapp.com/billing/top-up/success
                  cancel_url: https://yourapp.com/billing/top-up/cancel
      responses:
        '200':
          description: >-
            Stripe Checkout session created. Redirect the user to
            `checkout_session_url` to complete payment.
          content:
            application/json:
              schema:
                type: object
                required:
                  - checkout_session_id
                  - checkout_session_url
                properties:
                  checkout_session_id:
                    type: string
                    description: >-
                      Stripe Checkout Session identifier (e.g.
                      `cs_test_a1B2c3...`). Useful for server-side
                      reconciliation.
                  checkout_session_url:
                    type: string
                    format: uri
                    description: >-
                      Single-use Stripe-hosted payment page URL. Redirect the
                      customer here to complete payment.
              examples:
                Created:
                  value:
                    checkout_session_id: cs_test_a1B2c3D4e5F6g7H8i9J0
                    checkout_session_url: >-
                      https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0
        '400':
          description: >-
            Invalid payload — the amount is missing, not a number, below the $50
            minimum, or the organization is on an annual plan. The error body is
            a JSON array.
          content:
            application/json:
              examples:
                Missing amount:
                  value:
                    - amount_in_dollars is required.
                Not a number:
                  value:
                    - amount_in_dollars must be a number.
                Below minimum:
                  value:
                    - Amount must be at least $50.
                Annual plan:
                  value:
                    - >-
                      You have an annual plan. Contact your account manager to
                      top up.
        '403':
          description: >-
            Missing, invalid, or revoked API key, or the key cannot create
            top-up sessions for this organization. This endpoint returns `403`
            (never `401`) for authentication failures, including requests with
            no credentials at all.
          content:
            application/json:
              examples:
                Forbidden:
                  value:
                    detail: You do not have permission to perform this action.
        '429':
          description: >-
            Rate limit exceeded; back off and retry after the interval indicated
            in `Retry-After`.
          content:
            application/json:
              examples:
                Throttled:
                  value:
                    detail: Request was throttled. Expected available in 30 seconds.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: |-
            curl -X POST 'https://verification.didit.me/v3/billing/top-up/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "amount_in_dollars": 500,
                "success_url": "https://yourapp.com/billing/success",
                "cancel_url": "https://yourapp.com/billing/cancel"
              }'
        - lang: python
          label: Python
          source: |-
            import os

            import requests

            resp = requests.post(
                'https://verification.didit.me/v3/billing/top-up/',
                headers={
                    'x-api-key': os.environ['DIDIT_API_KEY'],
                    'Content-Type': 'application/json',
                },
                json={
                    'amount_in_dollars': 500,
                    'success_url': 'https://yourapp.com/billing/success',
                    'cancel_url': 'https://yourapp.com/billing/cancel',
                },
                timeout=10,
            )
            resp.raise_for_status()
            session = resp.json()
            print('Redirect customer to:', session['checkout_session_url'])
        - lang: javascript
          label: JavaScript
          source: >-
            const resp = await
            fetch('https://verification.didit.me/v3/billing/top-up/', {
              method: 'POST',
              headers: {
                'x-api-key': process.env.DIDIT_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                amount_in_dollars: 500,
                success_url: 'https://yourapp.com/billing/success',
                cancel_url: 'https://yourapp.com/billing/cancel',
              }),
            });

            if (!resp.ok) throw new Error(`Top-up failed: ${resp.status}`);

            const { checkout_session_url } = await resp.json();

            window.location.assign(checkout_session_url);
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````