# Stabo Developer Docs - Guides - [Overview](/en/docs/guide): Stabo public API product lines, integration steps, and documentation map. - [Quickstart](/en/docs/guide/quickstart): Prerequisites, environments, and your first request. - [Authentication](/en/docs/guide/authentication): Obtain an Access Token with the OAuth2 Client Credentials flow. - [Conventions](/en/docs/guide/conventions): Protocol, request headers, response format, data formats, and idempotency. - [Webhooks](/en/docs/guide/webhooks): Delivery mechanism, request headers, event structure, and signature verification. - [Best practices](/en/docs/guide/best-practices): Idempotency and retries, reconciliation and compensation, security advice, and checklists for integration testing and go-live. - **Product Guides** - [Checkout product guide](/en/docs/guide/checkout): Hosted checkout page, Payment Intent, Refund, Webhook events, and status reference. For endpoint details, see the API reference. - Business Collection - [Business Collection](/en/docs/guide/business-collection): Collect in local fiat currency and receive funds as stablecoins in your Stabo wallet. On-Ramp is currently available. - [On-Ramp](/en/docs/guide/business-collection/on-ramp): Buy USDT with Mexican pesos (MXN). Receive account opening and queries, quotes, order creation and queries, order statuses, and Webhook events. For endpoint details, see the API Reference. - [Payment product guide](/en/docs/guide/payment): Balance inquiry, transfers, transaction inquiry, SWIFT messages, fiat accounts, and credit notifications. See the API reference for endpoint details. - [Provider KYC and Account Opening](/en/docs/guide/provider-kyc): Complete KYB, RFI answers, and fiat account opening for a business customer through the LEGEND provider. Each step has request and response examples. - [Payment reference](/en/docs/guide/payment-reference): Charge bearer, purpose codes, transaction type codes, and country codes. - API Reference - [Open a receive account](/en/docs/api/business-collection/on-ramp/createOnRampReceiveAccount): Open a dedicated receiving CLABE for a payer. Opening takes a few minutes. Call this endpoint when the payer registers or is linked. Do not wait until payment time to open the account. This endpoint is idempotent. Repeated calls with the same `payerId` return the current status of the same receive account and do not open another account. `Idempotency-Key` is not required. Opening an account does not require payer KYC. You submit KYC when you create an order. This release does not push notifications for opening results. Use the Retrieve a receive account endpoint, and wait at least 1 minute between requests. The receive account does not expire. All future orders for the same payer use this CLABE. - Business Collection - On Ramp: Collect local fiat and convert it to USDT. Get quotes, create and retrieve orders, and open and retrieve receive accounts. - [Open a receive account](/en/docs/api/business-collection/on-ramp/createOnRampReceiveAccount): Open a dedicated receiving CLABE for a payer. Opening takes a few minutes. Call this endpoint when the payer registers or is linked. Do not wait until payment time to open the account. This endpoint is idempotent. Repeated calls with the same `payerId` return the current status of the same receive account and do not open another account. `Idempotency-Key` is not required. Opening an account does not require payer KYC. You submit KYC when you create an order. This release does not push notifications for opening results. Use the Retrieve a receive account endpoint, and wait at least 1 minute between requests. The receive account does not expire. All future orders for the same payer use this CLABE. - [Retrieve a receive account](/en/docs/api/business-collection/on-ramp/getOnRampReceiveAccount): Retrieve the opening status and CLABE of a payer's receive account. The response structure is the same as the Open a receive account response. You can only retrieve payers of your own merchant account. A `payerId` that has never been opened returns "receive account not found". - [Get a quote](/en/docs/api/business-collection/on-ramp/getOnRampQuote): Get a reference price. A quote does not lock the price. The final price is the one in the order creation response. Quotes support MXN amounts only. You can create an order by MXN (`fiatAmount`) or USDT (`cryptoAmount`) amount. A quote does not generate a quote ID. You do not need to pass quote information when you create an order. - [Create an order](/en/docs/api/business-collection/on-ramp/createOnRampOrder): Create an On-Ramp order and return payment instructions. On success, the endpoint returns HTTP 200 and the new order has `status` `AWAITING_PAYMENT`. The payer must transfer `fiatAmount` MXN to `paymentInfo.clabe` before `expiresAt`. The provider sets the order price in real time. It can differ from the quote. The order creation response is authoritative. Pass exactly one of `fiatAmount` and `cryptoAmount`. When you create an order by `fiatAmount`, the provider confirms the USDT amount to be credited. The credited amount is the `cryptoAmount` in the response. Before the provider confirms, `cryptoAmount` can be empty. An empty `paymentInfo` means the provider has not yet confirmed the collection. Retry the order creation with the same `Idempotency-Key`, or retrieve the order later. If the provider rejects the request, the order is set to `FAILED`. A replay with the same `Idempotency-Key` returns that `FAILED` order. Payer KYC is only forwarded to the provider. Stabo does not store it, and does not return the ID number or contact details in responses. The payer's receive account must already be open. See the Open a receive account endpoint. If it is not open, order creation returns "receive account being opened" and does not create an order. - [Retrieve an order](/en/docs/api/business-collection/on-ramp/getOnRampOrder): Retrieve an order by its Stabo order ID. The response structure is the same as the order creation response. You can only retrieve On-Ramp orders of your own merchant account. Other orders return "order not found". Use Webhooks as the primary channel and order queries as a fallback. If you poll, wait at least 30 seconds between requests, and stop when the order reaches a final status. If a Webhook and an order query disagree, the order query result is authoritative. - Checkout - Payment Intent: Create a Payment Intent, retrieve payment details, and list payments with pagination. - [List Payment Intents](/en/docs/api/checkout/payment-intent/listPaymentIntents): Lists Payment Intents with pagination. Pass filters as query parameters. - [Create a Payment Intent](/en/docs/api/checkout/payment-intent/createPaymentIntent): Creates a Payment Intent and generates a hosted checkout page link. After creation succeeds, redirect the customer to `checkoutUrl` in the response to complete the payment. Returns HTTP 200 on success. After creation, `status` is `AWAITING_PAYMENT`. - [Retrieve a Payment Intent](/en/docs/api/checkout/payment-intent/getPaymentIntent): Retrieves the payment details of a Payment Intent by its ID. - Refund: Create a refund, list refunds, and retrieve refund details. - [List Refunds](/en/docs/api/checkout/refund/listRefunds): Lists the refunds of the specified Payment Intent with pagination. - [Create a Refund](/en/docs/api/checkout/refund/createRefund): Creates a refund for the specified Payment Intent. Partial and full refunds are supported. If you omit `amount`, the remaining refundable amount is refunded. A refund first enters `PENDING_APPROVAL` and is executed after operations approves it. Returns HTTP 200 on success. - [Retrieve a Refund](/en/docs/api/checkout/refund/getRefund): Retrieves refund details by Refund ID. The refund must belong to the Payment Intent in the path. Otherwise, the request is handled as "not found". - Payment - Balance: Query the available balance of an account. - [Query balance](/en/docs/api/payment/balance/queryBalance): Query the available balance of an account. - Transfer: Intra-bank transfers, MEPS+ transfers, cross-border remittances, and their result queries. - [Intra-bank transfer](/en/docs/api/payment/transfer/createIntraBankTransfer): Intra-bank transfer between accounts. All currencies are supported. Use it to move funds between different accounts within the same bank. **Notes** - `remarks` supports only alphanumeric characters, spaces, and the special characters `/ - ? : ( ) . , ' +` - If the `PROCESSING` status is returned, wait 5 minutes and then call the result query endpoint to confirm the final status - For orders paid out via POBO, the response returns `poboAccountNo` / `poboAccountName`. When you call the result query endpoint, we recommend passing `accountNumber=poboAccountNo` to explicitly specify the POBO payout account. If `accountNumber` is not passed, the system automatically matches the corresponding POBO payout account based on `referenceNo`. - [Query intra-bank transfer result](/en/docs/api/payment/transfer/queryIntraBankTransfer): Query the result status of an intra-bank transfer. If the transfer returns the `PROCESSING` status, call this endpoint to confirm the final status. - [MEPS+ transfer](/en/docs/api/payment/transfer/createMepsTransfer): Make a real-time local transfer in Singapore through MEPS+ (MAS Electronic Payment System). Features: - SGD only - Real-time settlement - Suitable for high-value local transfers **Notes** - `beneficiaryName`, `beneficiaryAddress`, and `remarks` support only alphanumeric characters, spaces, and the special characters `/ - ? : ( ) . , ' +` - If the beneficiary bank is not a MEPS+ participant, provide the intermediary bank details - For orders paid out via POBO, the response returns `poboAccountNo` / `poboAccountName`. When you call the result query endpoint, we recommend passing `accountNumber=poboAccountNo` to explicitly specify the POBO payout account. If `accountNumber` is not passed, the system automatically matches the corresponding POBO payout account based on `referenceNo`. - [Query MEPS+ transfer result](/en/docs/api/payment/transfer/queryMepsTransfer): Query the result status of a MEPS+ transfer. - [Cross-border remittance](/en/docs/api/payment/transfer/createSwiftTransfer): Make a cross-border remittance through the SWIFT network. **Note**: The GLDB provider does not support cross-border remittances in SGD. **Notes (GLDB provider)** - SGD is not supported - If a BIC code has fewer than 11 characters, pad it with `X` - `beneficiaryName`, `beneficiaryAddress`, and `remarks` support only alphanumeric characters, spaces, and the special characters `/ - ? : ( ) . , ' +` - For orders paid out via POBO, the response returns `poboAccountNo` / `poboAccountName`. When you call the result query endpoint, we recommend passing `accountNumber=poboAccountNo` to explicitly specify the POBO payout account. If `accountNumber` is not passed, the system automatically matches the corresponding POBO payout account based on `referenceNo`. - [Query cross-border remittance result](/en/docs/api/payment/transfer/querySwiftTransfer): Query the result status of a cross-border remittance. - Transaction: Transaction history queries and MT/MX message queries. - [Query transaction history](/en/docs/api/payment/transaction/queryTransactionHistory): Query the transaction history of an account, with pagination. Use it for reconciliation and financial reporting. - [Query MT/MX message](/en/docs/api/payment/transaction/queryMtMxMessage): Query the raw MT/MX SWIFT message associated with a transfer. Use it for compliance audits and troubleshooting. For examples of `messageContent` in each scenario, see the [Payment product guide](/en/docs/guide/payment). - Fee: Query the estimated fee for a transfer. - [Query estimated transfer fee](/en/docs/api/payment/fee/estimateFee): Calculate the estimated fee based on the transfer type and amount. - Provider K Y C: Provider KYC. Requirements, entity KYB submission and status, and RFI answers. Only LEGEND is supported now. - [Get KYC requirements](/en/docs/api/payment/provider-kyc/getProviderKycRequirements): Returns the countries, options, and file requirements to submit the customer's entity KYC (KYB) to the provider. - `label` follows the request language and is for display only. Submit the `code`. - Without `customerUid`, `options` has 8 keys. - `options["documents[].category"]` lists reference values for the file category. It is not a complete list. Other values go to the provider unchanged, and the provider decides. - With `customerUid`, a customer that has a profile at the provider also gets `questionnaire.purposes`. For a new customer, the result has no `questionnaire.purposes`. - On the first submission of a new customer, `questionnaire.purposes` can use the reference values `invest_trade` and `crypto_fiat`. This is not a complete list. The provider checks the values. - The first call takes about 10 seconds. Later calls hit the cache and are faster. Set the client timeout to at least 60 seconds. For the full flow, see [Provider KYC and account opening](/en/docs/guide/provider-kyc). - [Get KYC status](/en/docs/api/payment/provider-kyc/getProviderKyc): Gets the customer's KYC status at the provider. It is not the Stabo platform KYC status. - `providerCustomerId` can be empty while `SUBMITTED`. It is returned after `APPROVED`. You need it to open an account. - `failureReason` is returned only when `REJECTED`. - [Submit KYB](/en/docs/api/payment/provider-kyc/submitProviderKyc): Submits or resubmits the customer's entity KYC to the provider. The first submission creates the customer at the provider. The request is `multipart/form-data`. The `data` part is the JSON data. Each other part is one file, and its part name equals a `documents[].clientFileId`. File parts and `clientFileId` values must match one to one. - If the provider already accepted a submission (`SUBMITTED` or `APPROVED`), the current status is returned. No data is written and there is no `submittedAt`. - `Idempotency-Key` is required. It links the request for audit. After a timeout, retry with the same data and the original key. - Depending on the number of files, the request can take more than 30 seconds. Set the client timeout to at least 60 seconds. - Stabo runs only the minimum checks to build the provider request and keep it safe: required fields are not empty, `data` has no unknown fields, `personId` and `clientFileId` do not repeat, file parts match, one file is at most 2 MB, and the file header is PDF, PNG, or JPEG. - The provider decides field formats, values, the number of persons per role, required files, and other rules. A failure returns `openapi.channel.rejected`. Lengths and values in the field descriptions are provider rules, checked by the provider. For a full example, see [Provider KYC and account opening](/en/docs/guide/provider-kyc#submit-kyb). - [List RFIs](/en/docs/api/payment/provider-kyc/listProviderKycRfis): Lists the requests for information (RFIs) the provider raised for the customer. The list does not include questions. Get an RFI to see them. - [Get an RFI](/en/docs/api/payment/provider-kyc/getProviderKycRfi): Gets one RFI with its questions. - [Create an IDV session](/en/docs/api/payment/provider-kyc/createProviderKycIdvSession): Creates an identity verification session for an IDV question of an RFI waiting for answers. - If the RFI is not `ACTION_REQUIRED`, `provider_kyc.rfi.not_actionable` is returned. If the question is not an IDV question of this RFI, `provider_kyc.rfi.question_not_idv` is returned. - `url` is a one-time session link. Give it only to the person who completes the verification. Do not log or forward it. - After the verification, still submit this question with the full RFI. - [Submit RFI answers](/en/docs/api/payment/provider-kyc/submitProviderKycRfiAnswers): Submits the answers to all questions of an RFI waiting for answers. The request is `multipart/form-data`. The `data` part is the JSON answers. Each other part is one file, and its part name equals a value in `answers[].clientFileIds`. - If the RFI is already under review (`UNDER_REVIEW`), the current status is returned. Nothing is written and there is no `submittedAt`. - `Idempotency-Key` is required. Reuse the original key when you retry the same submission. The provider does not accept it twice. - Acceptance is not approval and does not lift `restrictions`. - Stabo checks only that required fields are not empty, the safe characters of `clientFileId`, the file part match, and file size and type. The provider decides question ownership, required questions, length, file count, and selected values. A failure returns `openapi.channel.rejected`. For the answer format of each question type, see [Provider KYC and account opening](/en/docs/guide/provider-kyc#submit-rfi-answers). - Fiat Account: Standard fiat account opening, opening options, and account detail queries. - [Create a fiat account](/en/docs/api/payment/fiat-account/createFiatAccount): Create a fiat account. The request format depends on the provider: LEGEND uses `application/json`, and GLDB uses `multipart/form-data`. **LEGEND** - `Idempotency-Key` is required. - On synchronous success, the standard account is returned directly, and the status can be `ACTIVE`. If `PENDING` is returned, query the account detail. - If `fiat.account.channel.detail_unavailable` is returned, query with the account ID in the message. Do not open the account again. - A request body with undefined fields is rejected. **GLDB** - Do not send `Idempotency-Key`. If you send it, `fiat.account.idempotency_key.unsupported` is returned. - `currency` accepts only one currency. Multi-currency applications are not supported. - Account opening requires manual review. Create returns `PENDING`, and `accountId` is the application number. Use it to poll the account detail. After approval, the status changes to `ACTIVE`. - No Webhook is sent for status changes. If you send a field that belongs to the other provider, `fiat.account.field.unsupported` is returned. If a field that this provider requires is missing, `fiat.account.field.required` is returned. - [List opening options](/en/docs/api/payment/fiat-account/listFiatAccountOpeningOptions): List the available opening routes and fees for a customer. `currency` and `bankCountry` are both optional. Without both, all options of the customer are returned. `partnerBanks` are candidate banks only; the account detail shows the bank actually used. Applies to LEGEND only. GLDB does not support it and returns `openapi.channel.unsupported`. - [Retrieve an account](/en/docs/api/payment/fiat-account/getFiatAccount): Retrieve the account detail by account ID. **GLDB** - `accountId` is the application number returned by create. - Do not send `providerCustomerId`. - The status is `PENDING` during review and `ACTIVE` after approval. If the account is later deactivated or closed, the status is `INACTIVE` or `CLOSED`. If the review is rejected or the application is cancelled, the status is `FAILED`. - After the account is opened, `accountNumber`, `bankName`, `bankAddress`, `swiftBic`, `iban`, `bankCountry`, and `accountHolderName` are returned. `openedAt` is returned only when the status is `ACTIVE`. - No Webhook is sent for status changes. Poll this endpoint. - If the account does not exist, `fiat.account.not_found` is returned. Only single-currency accounts are supported. A query for a multi-currency account returns `fiat.account.multi_currency_unsupported`. - Help - [Error codes](/en/docs/help/errors): The error response format, how HTTP status codes map to the code in the response body, and how to handle each error. - [FAQ](/en/docs/help/faq): Common integration questions, and how to contact technical support. - [Changelog](/en/docs/help/changelog): Version changes of the public API documentation.