Skip to main content

Frequently Asked Questions

KYC & User Verification

  1. Create the User via the Create User endpoint.
  2. Verify the User using one of three options:
  3. Get the result via webhooks (VERIFIED_USER, REJECTED_KYC, REVIEW_NEEDED_KYC) or by polling.
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.
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.
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: LOZG7802117B9CURP is not accepted.
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.
FileMax SizeAllowed Formats
Selfie3 MBJPEG, PNG, GIF, WebP
Front ID / Back ID5 MB eachJPEG, PNG, GIF, WebP, PDF
Additional documents10 MBJPEG, PNG, GIF, WebP, 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.
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.
There are approximately 150 valid values. See the full list in the Submit KYC Data API reference. Common examples include DESARROLLO DE SOFTWARE, CONSULTORIA, FINANCIERA, EDUCACION, RESTAURANTE.
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.
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.

User Management

The API returns a 409 Conflict with error code USER_ALREADY_EXISTS_ERROR. Check for this status code to avoid duplicate user creation.
{
  "status": "failed",
  "code": "USER_ALREADY_EXISTS_ERROR",
  "message": "A user with this email or externalUserId already exists"
}
There are currently no API endpoints to update or delete users. For user deletion requests, contact derechosarco@capa.fi.

Webhooks

Failed webhook deliveries are retried up to 5 times with the following schedule:
AttemptDelay
1st retryImmediate
2nd retry1 minute
3rd retry5 minutes
4th retry10 minutes
5th retry15 minutes
If all attempts fail, the notification is marked as “Failed.” Return a 2xx status code promptly to acknowledge receipt.
There is currently no API endpoint or dashboard feature to replay missed webhook events. As a fallback, poll the List Transactions or Get KYC Details endpoints to check the latest status.
No. Each partner can register a single webhook URL per environment. Use the Webhook Settings endpoint to update it.
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.
Yes. When you receive the COMPLETED_ON_RAMP event, the crypto has been sent and is already in the user’s destination wallet.
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.
No. If a user is already verified, re-submitting KYC does not trigger a new webhook event.
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.

Quotes & Transactions

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.
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.
Yes. The Create Quote 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.
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%).
CurrencyMinimumMaximum
MXN50 MXNNo maximum
DOP150 DOPNo maximum
USD20 USDNo maximum
EUR20 EURNo maximum
These limits may change. There is no per-partner variation.
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 endpoint or by an admin.
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.
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.

On-Ramp

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 endpoint.
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.
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.
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.
Yes. Multiple on-ramp transactions can be created simultaneously for a single user.
No. The CLABE alone is sufficient. The account holder name auto-populates in the sender’s banking app when they enter the CLABE.

Off-Ramp

The excess crypto remains in the user’s wallet and is not included in the off-ramp transaction.
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.
Yes. The off-ramp fiat payout includes the transaction ID in the SPEI concepto field, allowing you to match payouts to specific off-ramp orders.
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.
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 endpoint.

Rate Limits & Errors

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.
All errors follow this structure:
{
  "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).

Sandbox & Testing

AspectStagingProduction
Fiat transfersSimulated (no real money)Real SPEI/ACH/SEPA transfers
Crypto transfersReal on-chain transactionsReal on-chain transactions
KYC verificationNot validated (random data accepted)Full identity verification
Exchange ratesMay differ from live marketLive market rates
Mock testingEnabledDisabled
Use the Mock Testing Guide to simulate transaction flows with special amounts (e.g., 1.01, 1.02, 1.03). Webhooks fire during mock testing if a webhook URL is configured.

Refunds

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.

KYB & Third-Party Payments

KYB is available as a link-based flow only. Use the Generate KYB Link endpoint to create a verification link for business users. Full API-driven KYB is not available due to the complexity of MX regulations.
Yes. Use the 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.

API Versioning

Yes. API v1 is officially deprecated. All new integrations should use v2 endpoints exclusively. If you are currently using v1 endpoints, migrate to v2 as soon as possible.