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

# B2B quickstart

> From zero to first settled payment in five steps.

This is the shortest path for a B2B integrator: keep your own ERP / invoicing, hand Caratuva the payment + KYC + cross-border settlement, and reconcile through webhooks. The full happy path takes minutes against test mode and is identical against live.

## What Caratuva does on this surface

* Accepts a payment intent from your server.
* Returns a hosted payment URL you forward to your buyer.
* Onboards the buyer through KYC (mandatory — no skip path).
* Collects payment from the buyer, runs cross-border transfer + FX, and pays out BRL via PIX to the seller's registered destination.
* Notifies your ERP through signed outbound webhooks at every state change.

You keep the customer relationship, the order management, and the line-item authority. Caratuva keeps the regulatory and settlement plumbing.

## Step 1 — Issue an API key

Log in to the dashboard and create a `pk_test_` key for development, plus a `pk_live_` key for production. Store both in your secret manager. The plaintext secret is surfaced exactly once at creation time.

See [API keys](/api-reference/api-keys) for the full CRUD surface.

## Step 2 — Register a webhook subscription

Before you create your first intent, subscribe to the `payment_intent.*` events you care about. The signing secret is also returned exactly once.

```bash theme={null}
curl -X POST https://api.caratuva.com/v1/webhook-subscriptions \
  -H "X-API-Key: $CARATUVA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "url": "https://erp.example.com/webhooks/caratuva",
    "eventTypes": [
      "payment_intent.created",
      "payment_intent.buyer_kyc_approved",
      "payment_intent.on_chain_confirmed",
      "payment_intent.settled",
      "payment_intent.failed",
      "payment_intent.cancelled",
      "payment_intent.expired"
    ]
  }'
```

See [Webhooks](/api-reference/webhooks) for the signature format, retry policy, and language-specific verifier examples.

## Step 3 — Create a payment intent

Each intent is a request to collect a specific amount from a specific buyer. `externalId` is your primary key — re-POSTing the same value is safe.

```bash theme={null}
curl -X POST https://api.caratuva.com/v1/payment-intents \
  -H "X-API-Key: $CARATUVA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{
    "externalId": "INV-2026-00042",
    "amount": "12500.00",
    "currency": "USD",
    "buyer": { "email": "buyer@acme.com", "name": "ACME Imports LLC", "country": "US" },
    "returnUrl": "https://erp.example.com/orders/42/paid",
    "metadata": { "orderId": "42" }
  }'
```

The response carries the field you actually need:

```json theme={null}
{
  "id": "ckabc...",
  "status": "awaiting_buyer",
  "hostedPaymentUrl": "https://pay.caratuva.com/r/ckpub..."
}
```

See [Payment intents](/api-reference/payment-intents) for the full request and response schema.

## Step 4 — Forward the payment link

Email, SMS, or render the `hostedPaymentUrl` in your own UI — whichever channel you already use to engage your buyer. Caratuva does not auto-email the buyer on the payment-intent surface, because you own the customer relationship.

The hosted page handles email magic-link sign-in, KYC, FX quoting, and payment collection. Your buyer never leaves `pay.caratuva.com` until the redirect to `returnUrl` after settlement.

## Step 5 — Reconcile through webhooks

Every state change fires a `payment_intent.*` event. The two events that matter for most ERPs:

* `payment_intent.settled` — funds are in the seller's BRL account. Mark the order paid in your system. The payload carries `paymentIntentId`, `externalId`, and the original `metadata` (plus `source` and `externalEventId`). For settlement amounts (`amount`, `currency`, `brlSettledAmount`, `fxRateAtSettlement`), fetch `GET /v1/payment-intents/{id}` on receipt — they are not on the webhook body.
* `payment_intent.failed` — terminal failure. The payload carries the reason. Refund the order or surface an error to your buyer.

Use `X-Caratuva-Delivery-Id` as an idempotency key — Caratuva retries up to seven times with exponential backoff (immediate, 30s, 2m, 10m, 1h, 6h, 24h) and your endpoint will see the same delivery id on every attempt.

## Test mode

`pk_test_` keys run the entire flow end-to-end against a sandbox payment instance. The same intent transitions, the same webhooks, the same response shapes — but KYB and buyer KYC auto-approve and no real money moves. Use it for CI and staging. See [Environments](/api-reference/environments).

## Common pitfalls

* **Missing buyer email.** It's required and must be a real address — that's where the magic-link goes.
* **Sending floats.** Pass `amount` as a string (`"12500.00"`) to avoid float drift.
* **Re-serializing the webhook body before verifying.** The signature is computed over the raw bytes. Always verify against `req.rawBody`, not `JSON.stringify(req.body)`.
* **Reusing `Idempotency-Key` across distinct logical operations.** It's scoped per organization and collapses retries; don't share it across unrelated intents.

## Where to next

* [Payment intents](/api-reference/payment-intents) — full schema, error codes, state machine.
* [Webhooks](/api-reference/webhooks) — signature verification, retry policy, `payment_intent.*` event reference.
* [API keys](/api-reference/api-keys) — issuance, rotation, revocation.
