Best practices

Idempotency and retries, reconciliation and compensation, security advice, and checklists for integration testing and go-live.

This page describes common practices for integrating financial APIs. For Stabo behavior, the Conventions, Webhooks, and the product guides are the source of truth.

Idempotency and retries

  • Send an Idempotency-Key with every POST request.
  • If a request times out or you receive no response, retry the same request with the same Idempotency-Key. The Payment transfer endpoints are an exception: a duplicate key is rejected. Query the result with referenceNo instead. See Payment idempotency.
  • Use a different Idempotency-Key for each different business request. You can derive it from the order number, or use a UUID.
  • Retry with exponential backoff. Double the interval after each retry, add a small random jitter, and set a maximum number of retries.
  • After you reach the retry limit, do not keep retrying blindly. Confirm the result with a query endpoint, then decide whether to handle it manually.
  • On a 401, get a new token and retry.
  • On a 400, the request itself has a problem. Fix it according to the message in the response. Do not retry it unchanged.

Reconciliation and compensation

Webhooks are the main way to receive results. Query endpoints are for compensation and reconciliation.

  • Treat the status returned by a query endpoint as final. If a Webhook and a query result differ, use the query result.
  • Webhooks can be duplicated or arrive out of order. Deduplicate by Webhook-Event-Id. To decide the order status, read data.object.status. Do not rely only on the event type.
  • For orders that stay in a non-final status for a long time, call the query endpoint on a schedule.
  • Reconcile every day. Pull the list by date range with pagination, compare status and the amount with each local order, and record the differences for follow-up.
  • Store and compare amounts as strings or Decimals. Do not use floating-point numbers. Apply the currency precision when you compare.
  • Record the traceId of every request. When a reconciliation difference occurs and you contact technical support, provide the traceId.

Security advice

  • Keep client_secret, access tokens, and Webhook secrets on the server only. Store them in environment variables or a secrets manager. Do not put them in code, frontend apps, mobile apps, or code repositories.
  • Rotate keys regularly: add a new key in the Dashboard, switch to the new key, then delete the old key. If you suspect a leak, replace the key the same way right away and delete the old key.
  • You must verify Webhook signatures. Compute the signature from the raw request body, compare in constant time, and check the timestamp to prevent replay. See Webhooks.
  • The Webhook callback URL must use HTTPS.
  • Configure an IP allowlist for each key in the Dashboard. Enter only the egress IPs of the servers that actually call Stabo. If you leave it empty, any source IP is allowed. When your egress IP changes, update the allowlist.
  • Do not log client_secret, access tokens, Webhook secrets, or full account information. To troubleshoot, log only identifiers such as traceId and the order number.
  • Cache and reuse the same token in your application. Do not get a new token for every request.

Integration and go-live checklists

Integrate in the test environment

Use the test environment Base URL https://api.test.stabofx.com and the test environment credentials.

Get an access token and confirm that you receive access_token.

Call a business endpoint. For Checkout, create a Payment Intent and get the checkoutUrl.

Confirm that your Webhook URL receives notifications, and use the fixed sample to confirm that your signature verification code is correct.

Query the order with the query endpoint and confirm that the status matches the Webhook.

Before go-live

  • The production Base URL, client_id, client_secret, and tenant_id are in place, and are stored on the server only.
  • The egress IPs of the production servers are on the allowlist.
  • The Webhook URL is a publicly accessible HTTPS address.
  • Webhook handling returns 2xx first, then processes the business logic asynchronously, and responds within 30 seconds.
  • Webhook signatures are verified: raw request body, constant-time comparison, timestamp tolerance.
  • Webhooks are deduplicated by Webhook-Event-Id, and business handling is idempotent.
  • Every POST request carries an Idempotency-Key, and retries reuse the same key. After a Payment transfer times out, query the result with referenceNo and do not submit again.
  • Amounts are strings or Decimals. No floating-point numbers are used.
  • The request timeout is 30 seconds, and exponential backoff retry is implemented.
  • The token is cached, and a new token is obtained on a 401.
  • A scheduled re-query job and a daily reconciliation job exist.
  • Logs are masked and record the traceId.

On this page