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

# ❓ FAQ

> Frequently asked questions about integrating with the Capa API.

***

# Frequently Asked Questions

<AccordionGroup>
  ## KYC & User Verification

  <Accordion title="What is the correct KYC flow order?">
    1. **Create the User** via the [Create User](/reference/userpartnercontrollerv2_createuser) endpoint.
    2. **Verify the User** using one of three options:
       * **Option A** — Redirect to a KYC verification link via [Generate KYC Link](/reference/userpartnercontrollerv2_createverificationlink)
       * **Option B** — Submit identity documents directly via [Submit KYC Data](/reference/userpartnercontrollerv2_createuserkyc)
       * **Option C** — For business users, generate a KYB link via [Generate KYB Link](/reference/userpartnercontrollerv2_generatekybverificationlink)
    3. **Get the result** via [webhooks](/docs/kyc-events) (`VERIFIED_USER`, `REJECTED_KYC`, `REVIEW_NEEDED_KYC`) or by [polling](/reference/kycpartnercontrollerv2_getuserverificationdetails).

    The `documents` field in the KYC endpoint is for `ADDRESS_PROOF` only — government ID front/back goes in the `identity` object (`frontIdUrl`, `backIdUrl`). The selfie is a separate top-level `selfieUrl` field.
  </Accordion>

  <Accordion title="What identity document types are accepted per country?">
    For the **direct KYC API** (`identity.type`), the accepted values are `GOVERNMENT_ID`, `RESIDENT_CARD`, and `TAX_ID` for all countries.

    For the **file-based KYC upload** flow, the accepted document types are `NATIONAL_ID`, `VOTER_ID`, `PASSPORT`, and `TAX_ID` for all countries including Mexico.
  </Accordion>

  <Accordion title="What is the correct taxId format for Mexico?">
    The `taxId` field expects an **RFC** (Registro Federal de Contribuyentes), not a CURP. The format must match:

    ```
    ^[A-Z]{4}\d{6}[A-Z0-9]{3}$
    ```

    Example: `LOZG7802117B9`

    CURP is **not** accepted.
  </Accordion>

  <Accordion title="Is ADDRESS_PROOF always required for Mexican users?">
    No. The `documents` array with `ADDRESS_PROOF` is required only if the identity document used is **not** an INE. If the user submits an INE and their address matches their current address, `ADDRESS_PROOF` can be omitted.
  </Accordion>

  <Accordion title="What are the file URL requirements for document uploads?">
    | File                 | Max Size  | Allowed Formats     |
    | -------------------- | --------- | ------------------- |
    | Selfie               | 3 MB      | JPEG, JPG, PNG      |
    | Front ID / Back ID   | 5 MB each | JPEG, JPG, PNG, PDF |
    | Additional documents | 10 MB     | JPEG, JPG, PNG, PDF |

    All URLs must be publicly accessible or pre-signed with a **minimum TTL of 30 minutes**. Short-lived signed URLs (a few seconds) will fail with `FAILED_TO_DOWNLOAD_FILE_FROM_PRESIGNED_URL`.
  </Accordion>

  <Accordion title="Are real documents required in sandbox?">
    No. In the staging environment, KYC verification does not perform real identity checks. You can submit random or test data — documents do not need to be genuine.
  </Accordion>

  <Accordion title="What are the valid economicActivity values?">
    There are approximately 150 valid values. See the full list in the [Submit KYC Data API reference](/reference/userpartnercontrollerv2_createuserkyc). Common examples include `DESARROLLO DE SOFTWARE`, `CONSULTORIA`, `FINANCIERA`, `EDUCACION`, `RESTAURANTE`.
  </Accordion>

  <Accordion title="When are realOwner, beneficiaryOwner, and resourceProvider required?">
    These are optional MX-only fields. They are only needed when the person performing the transaction is **different from the user** — for example, if someone else is the beneficial owner of the funds. If the user is both the sender and the receiver, these fields can be omitted.
  </Accordion>

  <Accordion title="Which countries are currently supported?">
    All countries listed in the API spec are live: MX, DO, US, and EU/SEPA countries (AT, BE, BG, HR, CY, CZ, DK, EE, FI, FR, DE, GR, HU, IE, IT, LV, LT, LU, MT, NL, PL, PT, RO, SK, SI, ES, SE, IS, LI, NO, CH, GB, MC, SM, AD, VA). CN (China) is coming soon for off-ramps only.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## User Management

  <Accordion title="What happens if I create a user that already exists?">
    The API returns a `409 Conflict` with error code `USER_ALREADY_EXISTS_ERROR`. Check for this status code to avoid duplicate user creation.

    ```json theme={null}
    {
      "status": "failed",
      "code": "USER_ALREADY_EXISTS_ERROR",
      "message": "A user with this email or externalUserId already exists"
    }
    ```
  </Accordion>

  <Accordion title="Can users be updated or deleted via API?">
    There are currently no API endpoints to update or delete users. For user deletion requests, contact [derechosarco@capa.fi](mailto:derechosarco@capa.fi).
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Webhooks

  <Accordion title="What is the webhook retry policy?">
    Failed webhook deliveries are retried up to **5 times** with the following schedule:

    | Attempt   | Delay      |
    | --------- | ---------- |
    | 1st retry | Immediate  |
    | 2nd retry | 1 minute   |
    | 3rd retry | 5 minutes  |
    | 4th retry | 10 minutes |
    | 5th retry | 15 minutes |

    If all attempts fail, the notification is marked as "Failed." Return a `2xx` status code promptly to acknowledge receipt.
  </Accordion>

  <Accordion title="Can I replay missed webhook events?">
    There is currently no API endpoint or dashboard feature to replay missed webhook events. As a fallback, poll the [List Transactions](/reference/transactionpartnerv2controller_listpartnerusertransactions) or [Get KYC Details](/reference/kycpartnercontrollerv2_getuserverificationdetails) endpoints to check the latest status.
  </Accordion>

  <Accordion title="Can I configure more than one webhook URL?">
    No. Each partner can register a **single webhook URL** per environment. Use the [Webhook Settings](/reference/partnerwebhooksettingsv2controller_update) endpoint to update it.
  </Accordion>

  <Accordion title="Do webhooks fire during mock testing?">
    Yes. If a webhook URL is configured in the staging environment, webhooks will fire during mock testing, allowing you to test your webhook handling end-to-end.
  </Accordion>

  <Accordion title="When COMPLETED_ON_RAMP fires, is the crypto already in the wallet?">
    Yes. When you receive the `COMPLETED_ON_RAMP` event, the crypto has been sent and is already in the user's destination wallet.
  </Accordion>

  <Accordion title="What does FIAT_RECEIVED_ON_RAMP mean exactly?">
    `FIAT_RECEIVED_ON_RAMP` means that fiat has been received and the conversion to crypto has **started** — but the crypto has **not been sent yet**. Wait for `COMPLETED_ON_RAMP` to confirm delivery.
  </Accordion>

  <Accordion title="Does a webhook fire when KYC is re-submitted for an already-verified user?">
    No. If a user is already verified, re-submitting KYC does not trigger a new webhook event.
  </Accordion>

  <Accordion title="What webhook events are available for bank account verification?">
    Two events fire when bank account verification completes:

    * `VERIFIED_BANK_ACCOUNT` — The bank account was successfully verified.
    * `REJECTED_BANK_ACCOUNT` — The bank account verification was rejected.

    These use `type: "USER"` and include the `userBankInfoId` in the payload.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Quotes & Transactions

  <Accordion title="How long is a quote valid?">
    A locked quote is valid for **10 seconds** after creation (see the `expiresAt` field in the response). Once a quote is used to create a transaction, the rate is **locked** for that transaction regardless of the 10-second window.
  </Accordion>

  <Accordion title="Is a quote required before creating a transaction?">
    No. The `quoteId` parameter is optional when creating an on-ramp, off-ramp, or cross-ramp transaction. If no quote is provided, the exchange rate is calculated at the time of funding. A quote is useful for showing the user a guaranteed rate before they commit.
  </Accordion>

  <Accordion title="Can I quote by specifying either fiat or crypto amount?">
    Yes. The [Create Quote](/reference/quotepartnerv2controller_createpartnerquote) endpoint accepts either `fiatAmount` or `cryptoAmount`. Use `fiatAmount` to get how much crypto the user will receive, or `cryptoAmount` to get how much fiat is needed.
  </Accordion>

  <Accordion title="How does premiumSpread work with quotes?">
    When a `quoteId` is provided in the transaction request, the **quote's `premiumSpread`** is used and any `premiumSpread` in the transaction request body is ignored. Always set `premiumSpread` when creating the quote to ensure consistent pricing. The valid range is `0` to `1` (representing 0% to 100%).
  </Accordion>

  <Accordion title="What are the transaction limits?">
    | Currency | Minimum | Maximum    |
    | -------- | ------- | ---------- |
    | MXN      | 50 MXN  | No maximum |
    | DOP      | 150 DOP | No maximum |
    | USD      | 20 USD  | No maximum |
    | EUR      | 20 EUR  | No maximum |

    These limits may change. There is no per-partner variation.
  </Accordion>

  <Accordion title="Do transactions expire?">
    No. Once a transaction is created, it does not have an expiration time or `expires_at` field. Transactions can only be cancelled via the [Cancel Transaction](/reference/transactionpartnerv2controller_cancelpartnertransaction) endpoint or by an admin.
  </Accordion>

  <Accordion title="Is idempotency supported?">
    No. The API does not currently support idempotency keys. Sending the same request twice will create two separate transactions. Implement safeguards in your integration to prevent duplicate submissions.
  </Accordion>

  <Accordion title="What rounding rules apply?">
    Fiat amounts are rounded to **2 decimal places**. Crypto amounts are rounded to **6 decimal places**. If a partner sends a ceiling-rounded crypto amount on an off-ramp, the difference remains in the user's wallet.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## On-Ramp

  <Accordion title="When is the CLABE created?">
    For Mexico, a dedicated CLABE (bank account identifier) is **automatically created** when the user's KYC is verified — no separate API call is needed. The CLABE is returned in the on-ramp creation response under `bankAccount.accountIdentifier` and can also be retrieved via the [Get Bank Info](/reference/userbankinfopartnerv2controller_getuserbankinfosbyuserid) endpoint.
  </Accordion>

  <Accordion title="Is the CLABE permanent and reusable?">
    Yes. The CLABE is permanent and reusable across all on-ramp transactions for the user. It does not expire or change unless the user is deactivated or the partner migrates banks.
  </Accordion>

  <Accordion title="Must I create an on-ramp before depositing fiat?">
    Yes. An on-ramp transaction **must be created before** making the fiat deposit. Deposits received without a matching active on-ramp order will be refunded at end of business day.
  </Accordion>

  <Accordion title="How do I fund a specific on-ramp order?">
    For MXN (SPEI) deposits, include the **transaction ID** (with or without dashes) in the SPEI **concepto** field. This matches the deposit to the correct order. If no transaction ID is in the concepto, the deposit is matched by **amount + CLABE**. If multiple orders share the same amount, the oldest order is matched first. If neither method matches an open order, the deposit is refunded at end of business day. For DOP, USD, and EUR deposits, matching is handled internally by Capa.
  </Accordion>

  <Accordion title="Can I have multiple on-ramp orders open at the same time?">
    Yes. Multiple on-ramp transactions can be created simultaneously for a single user.
  </Accordion>

  <Accordion title="Is the account holder name needed for SPEI deposits?">
    No. The CLABE alone is sufficient. The account holder name auto-populates in the sender's banking app when they enter the CLABE.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Off-Ramp

  <Accordion title="What happens if I send more crypto than the quoted amount?">
    The excess crypto remains in the user's wallet and is not included in the off-ramp transaction.
  </Accordion>

  <Accordion title="What are the expected settlement times?">
    For MXN off-ramp payouts via SPEI:

    * Under 50,000 MXN: \~2–3 minutes
    * Over 50,000 MXN: 10–30 minutes

    For forwarded OTC quotes using `forwardingDays` (1–4), the payout is scheduled on settlement day T+N; the 10–30 minute window applies once that day arrives.
  </Accordion>

  <Accordion title="Does the SPEI payout include a reference?">
    Yes. The off-ramp fiat payout includes the **sanitized transaction ID** (with dashes, the `-` character, removed) in the SPEI concepto field, allowing you to match payouts to specific off-ramp orders. For example, a transaction ID of `f51f2332-c7d4-40eb-8fb2-11e1608bc24c` appears in the concepto as `f51f2332c7d440eb8fb211e1608bc24c`.
  </Accordion>

  <Accordion title="What does 'partner is not configured for manual off-ramp execution' mean?">
    This error means the Execute Off-Ramp feature has not been enabled for your partner account. This feature is required for forwards-enabled partners. Contact the Capa development team to request enablement.
  </Accordion>

  <Accordion title="How do forwards work?">
    Forwards allow partners to lock a rate now and settle the transaction T+N days later. Use the `forwardingDays` parameter (0–4) when creating a locked quote for MXN OTC orders. This feature requires the manual off-ramp execution flow to be enabled — all off-ramps must be explicitly executed via the [Execute Off-Ramp](/reference/offramppartnerv2controller_executeofframp) endpoint.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Rate Limits & Errors

  <Accordion title="What are the API rate limits?">
    All API endpoints are rate-limited to **10 requests per second** per partner. Exceeding this limit returns a `429 Too Many Requests` response. The limits are the same across all endpoints. Rate limit information is not included in response headers.
  </Accordion>

  <Accordion title="What does the error response format look like?">
    All errors follow this structure:

    ```json theme={null}
    {
      "status": "failed",
      "code": "INVALID_USER_INPUT",
      "message": "Description of what went wrong",
      "errors": [
        { "field": "email", "message": "must be a valid email" }
      ]
    }
    ```

    Common HTTP status codes: `400` (validation), `401` (unauthorized), `403` (forbidden), `404` (not found), `409` (conflict/duplicate), `429` (rate limit).
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Sandbox & Testing

  <Accordion title="What are the differences between staging and production?">
    | Aspect           | Staging                              | Production                   |
    | ---------------- | ------------------------------------ | ---------------------------- |
    | Fiat transfers   | Simulated (no real money)            | Real SPEI/ACH/SEPA transfers |
    | Crypto transfers | Real on-chain transactions           | Real on-chain transactions   |
    | KYC verification | Not validated (random data accepted) | Full identity verification   |
    | Exchange rates   | May differ from live market          | Live market rates            |
    | Mock testing     | Enabled                              | Disabled                     |
  </Accordion>

  <Accordion title="How do I test transactions in staging?">
    Use the [Mock Testing Guide](/reference/transactionpartnerv2controller_getmocktestingguide) to simulate transaction flows with special amounts (e.g., `10.01`, `10.02`, `10.03`). Webhooks fire during mock testing if a webhook URL is configured.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## Refunds

  <Accordion title="Is there a refund endpoint?">
    No. There is no self-service refund endpoint. If a refund is needed (e.g., a deposit without a matching on-ramp order), Capa's FX agents will get in contact to handle the refund process.
  </Accordion>
</AccordionGroup>

<AccordionGroup>
  ## KYB & Third-Party Payments

  <Accordion title="Is KYB available via API?">
    KYB is available as a **link-based flow only**. Use the [Generate KYB Link](/reference/userpartnercontrollerv2_generatekybverificationlink) endpoint to create a verification link for business users. Full API-driven KYB is not available due to the complexity of MX regulations.
  </Accordion>

  <Accordion title="Does Capa support third-party payments?">
    Yes. Use the [Receivers](/docs/receivers) feature to send off-ramp or cross-ramp payouts to third parties. Create a receiver (individual or business), link bank accounts to them, and pass the `receiverId` when creating an off-ramp or cross-ramp transaction.
  </Accordion>
</AccordionGroup>
