Developers

Build on Stratos.

Checkout, payment links and webhooks. Sandbox today.

Sandbox preview · not live production keys
create-payment · sandbox
curl -X POST https://www.stratosfin.io/api/v1/payments \
  -H "Authorization: Bearer sf_test_…" \
  -H "Idempotency-Key: ORD-1042" \
  -H "Content-Type: application/json" \
  -d '{"amount":25,"currency":"USD","description":"Order 1042","reference":"ORD-1042"}'
answer
{
  "id": "pay_…",
  "object": "payment",
  "ref": "SF-…",
  "livemode": false,
  "status": "succeeded",
  "amount": 25,
  "amount_refunded": 0,
  "currency": "USD",
  "capture": "automatic",
  "rail": "prc_sandbox_a",
  "reference": "ORD-1042"
}

A real sandbox call. Keys and IDs are placeholders; no money moves.

Integration surface

What you can build.

  • Payments API
  • Payment links
  • Pay with Stratos button
  • Webhooks
  • Hosted KYC
  • Plugins (roadmap)

Base: https://stratosfin.io

Quickstart

A sandbox payment in six steps.

  1. 1

    Open an account

    Sign up as a business. The sandbox works while the team reviews your application.

  2. 2

    Make a sandbox key

    One click under API keys. It starts sf_test_ and is shown once; keep it in your server's secrets.

  3. 3

    Create a payment

    Send the key as a bearer token. The sandbox approves it and answers with the payment.

    curl -X POST https://www.stratosfin.io/api/v1/payments \
      -H "Authorization: Bearer sf_test_…" \
      -H "Idempotency-Key: ORD-1042" \
      -H "Content-Type: application/json" \
      -d '{"amount":25,"currency":"USD","description":"Order 1042","reference":"ORD-1042"}'
  4. 4

    Read it back

    List your payments, or open one by its id. It also shows under Transactions as a sandbox run.

    curl https://www.stratosfin.io/api/v1/payments \
      -H "Authorization: Bearer sf_test_…"
  5. 5

    Try a decline and a refund

    An amount ending in .01 is declined, so you can handle failures; then refund the first payment.

    curl -X POST https://www.stratosfin.io/api/v1/payments \
      -H "Authorization: Bearer sf_test_…" \
      -H "Idempotency-Key: ORD-1043" \
      -H "Content-Type: application/json" \
      -d '{"amount":25.01,"currency":"USD","description":"Order 1042","reference":"ORD-1043"}'
    
    curl -X POST https://www.stratosfin.io/api/v1/payments/pay_…/refund \
      -H "Authorization: Bearer sf_test_…" \
      -H "Idempotency-Key: order-1042" \
      -H "Content-Type: application/json"
  6. 6

    Listen for events

    Add a webhook endpoint under Webhooks. Each event is signed; check the signature before you trust it.

Checkout

A button on your page.

  1. Make a publishable key under API keys (kind: Publishable), with your website listed.
  2. Paste the script and the button; set the amount, currency and your reference.
  3. Listen for stratos:checkout, or let success_url bring the customer back.
<script src="https://www.stratosfin.io/checkout.js" defer></script>
<button
  data-stratos-key="pk_test_…"
  data-amount="25"
  data-currency="USD"
  data-description="Order 1042"
  data-reference="ORD-1042"
  data-success="https://shop.example/thanks"
  data-cancel="https://shop.example/cart"
>
  Pay with Stratos
</button>
document.querySelector("[data-stratos-key]").addEventListener("stratos:checkout", (e) => {
  // e.detail = { status: "complete" | "failed" | "cancelled", session: "cs_…", reference }
  if (e.detail.status === "complete") location.assign("/thanks?order=ORD-1042");
});

Reference

Every call, one description.

CallScopeWhat it does
POST/api/v1/paymentspayments:writeCreate a payment. Routes the payment to a rail and answers with its status. In the sandbox an amount ending in .01 is declined and .02 is a rail error (the router fails over); anything else is approved.amount* · currency* · method · country · capture · description · reference · merchant_id · card_token · wallet_mobile · return_url · customer_email · customer_ip · device_id · bank_routing_number · bank_account_number · bank_account_name · bank_account_type · sec_code
GET/api/v1/paymentspayments:readList payments. Your payments in this key's environment, newest first.
GET/api/v1/payments/{id}payments:readGet a payment. One payment by its id.
POST/api/v1/payments/{id}/capturepayments:writeCapture a payment. Settles a payment created with capture: manual.
POST/api/v1/payments/{id}/voidpayments:writeVoid a payment. Cancels a payment that is waiting for capture.
POST/api/v1/payments/{id}/refundrefunds:writeRefund a payment. Refunds all of a settled payment, or part of it when you give an amount.amount
POST/api/v1/fx/quotepayouts:writeQuote a conversion. An indicative rate for a payout in another currency; the licensed partner books the rate.base* · quote* · amount*
POST/api/v1/payoutspayouts:writeCreate a payout. An instruction to pay out to an account. Recorded only: no money moves until a licensed partner rail is live.amount* · currency* · rail* · account* · account_title* · fx_quote · reference · merchant_id
GET/api/v1/payoutspayments:readList payouts. Your payouts in this key's environment, newest first.
GET/api/v1/payouts/{id}payments:readGet a payout. One payout by its id. The account is masked.
POST/api/v1/checkout/sessionspayments:writeOpen a checkout session. Opens a hosted checkout page for one payment: the answer's url is where you send the customer. They choose the method there (card, wallet, a US bank account), the payments core routes it, and they are sent to success_url or cancel_url with ?stratos_session=<id>&status=. A session lives a day. Your endpoints hear checkout.session.completed beside the payment's own events.amount · currency* · line_items · splits · description · reference · customer_name · customer_email · success_url · cancel_url · country · promo_code · merchant_id
GET/api/v1/checkout/sessionspayments:readList checkout sessions. Your sessions, newest first.
GET/api/v1/checkout/sessions/{id}payments:readRead a checkout session. Its status (open, requires_action, processing, complete, failed, expired, cancelled) and payment_id once paid.
POST/api/v1/checkout/sessions/{id}/expirepayments:writeCancel an open checkout session. An open or not-yet-paid session becomes cancelled; the page stops taking payment.
POST/api/v1/sellerspayouts:writeAdd a seller. A seller under your platform: who they are and, optionally, the account you pay them to. A seller's share of a payment is a transfer; sellers are paid from what they are owed.name* · email · phone · country · share_pct · payout_rail · payout_account · payout_account_title · external_id · merchant_id
GET/api/v1/sellerspayments:readList sellers. Your sellers with what each is owed, held and paid, newest first.
GET/api/v1/sellers/{id}payments:readGet a seller. One seller by its id, with what they are owed.
POST/api/v1/transferspayouts:writeTransfer part of a payment to a seller. Records a seller's share of a succeeded payment, after the fact. A checkout session with splits does this by itself when the customer pays. The shares of one payment never exceed what is left of it.payment* · seller* · amount* · reason · reference
GET/api/v1/transferspayments:readList transfers. Your transfers in this key's environment, newest first.
GET/api/v1/transfers/{id}payments:readGet a transfer. One transfer by its id.
POST/api/v1/transfers/{id}/reversepayouts:writeReverse a transfer. Takes a seller's share back while they have not been paid it.reason
POST/api/v1/billing/planspayments:writeCreate a plan. A price every day, week, month or year in your currency, with an optional free trial.name* · description · amount* · currency · interval* · interval_count · trial_days · merchant_id
GET/api/v1/billing/planspayments:readList plans. Your plans with how many subscribers each has.
POST/api/v1/subscriptionspayments:writeSubscribe a customer. Signs a customer up on a plan with a saved card token; the first period is charged at once (unless the plan has a trial) and the billing run charges each period after that, trying failed charges again after one, three and seven days. Your endpoints hear subscription.created, subscription.paid, subscription.past_due and subscription.cancelled.plan* · customer_name* · customer_email* · card_token* · reference
GET/api/v1/subscriptionspayments:readList subscriptions. Your subscriptions, newest first.
GET/api/v1/subscriptions/{id}payments:readGet a subscription. One subscription by its id.
POST/api/v1/subscriptions/{id}/pausepayments:writePause a subscription. No charges until it is resumed.
POST/api/v1/subscriptions/{id}/resumepayments:writeResume a subscription. The time paused is added to the period, so nothing is charged twice.
POST/api/v1/subscriptions/{id}/cancelpayments:writeCancel a subscription. No further charges. The reason is kept with the subscription.reason
POST/api/v1/invoicespayments:writeCreate an invoice. A numbered invoice to a customer: lines with their tax rates, a discount shared across the lines before tax, a due date (your days-due setting after the issue date when none is given). The answer's url is the pay page, where the customer pays by card, wallet or bank account through the hosted checkout, in full or in parts. Send it with send: true, or later with /send. Your endpoints hear invoice.sent, invoice.payment_recorded, invoice.paid, invoice.overdue and invoice.voided.customer_name* · customer_email · customer_address · customer_tax_id · lines* · currency · discount · issue_date · due_date · reference · notes · terms · allow_partial · send · merchant_id
GET/api/v1/invoicespayments:readList invoices. Your invoices, newest first, each with what is paid and what is left.
GET/api/v1/invoices/{id}payments:readGet an invoice. One invoice by its id.
POST/api/v1/invoices/{id}/sendpayments:writeSend an invoice. A draft becomes open to pay; the customer gets the pay link by email. Sending again re-sends the mail.
POST/api/v1/invoices/{id}/voidpayments:writeVoid an invoice. An invoice nothing has been paid on stops being payable.reason
POST/api/v1/invoices/{id}/paymentspayments:writeRecord a payment on an invoice. Money that arrived outside the pay link (a bank transfer, cash, a cheque), in part or in full; the invoice becomes partly paid or paid.amount* · method · note
POST/api/v1/promo-codespayments:writeCreate a promo code. A code a customer enters on the hosted checkout (also one a payment link opens) or you apply to an invoice: a percentage or a fixed amount off, with an optional minimum order, a cap on a percentage, validity dates, a total number of uses and uses per customer. Each paid order with the code is a redemption.code* · kind · value* · min_amount · max_discount · starts_at · ends_at · max_uses · per_customer · surfaces · description · merchant_id
GET/api/v1/promo-codespayments:readList promo codes. Your codes, newest first, each with its state, uses and the discount given.
GET/api/v1/promo-codes/{id}payments:readGet a promo code. One code by its id.
POST/api/v1/promo-codes/{id}/archivepayments:writeArchive a promo code. The code stops working for good; its redemptions stay on record.

Snippets

The same call, in your language.

POST /api/v1/payments · Create a payment
// From your own server, never a browser: the key is a secret
const res = await fetch("https://www.stratosfin.io/api/v1/payments", {
  method: "POST",
  headers: {
    Authorization: "Bearer sf_test_…",
    "Content-Type": "application/json",
    "Idempotency-Key": "ORD-1042",
  },
  body: JSON.stringify({
    "amount": 25,
    "currency": "USD",
    "description": "Order 1042",
    "reference": "ORD-1042"
  }),
});
const data = await res.json();
console.log(res.status, data.status);
POST /api/v1/checkout/sessions · Open a checkout session
// From your own server, never a browser: the key is a secret
const res = await fetch("https://www.stratosfin.io/api/v1/checkout/sessions", {
  method: "POST",
  headers: {
    Authorization: "Bearer sf_test_…",
    "Content-Type": "application/json",
    "Idempotency-Key": "ORD-1042",
  },
  body: JSON.stringify({
    "currency": "USD",
    "description": "Order 1042",
    "reference": "ORD-1042"
  }),
});
const data = await res.json();
console.log(res.status, data.status);

Sandbox scenarios

Every path, on purpose.

ScenarioHowExpect
An approved card paymentAny amount not ending in .01 or .02 on the Stratos sandbox rail; card 4242 4242 4242 4242 (tok_visa) on the sandbox acquirer.status succeeded (automatic capture) or requires_capture (capture: manual); payment.settled to your endpoints.
A declineAn amount ending in .01 on the Stratos sandbox rail; card 4000 0000 0000 0002 (tok_decline) or 4000 0000 0000 9995 (tok_insufficient) on the acquirer.402 with status failed and the failure text; on the hosted page the customer may try another card.
A rail error and failoverAn amount ending in .02, or card 4000 0000 0000 0119 (tok_error): the first rail errors and the router tries the next.The payment lands on the next rail that answers; the attempts list on Payments core shows both.
3-D SecureCard 4000 0000 0000 3220 (tok_3ds): the acquirer asks the customer to confirm; code 123456 passes.202 with status requires_action and next_action.url; the hosted page sends the customer there and takes them back.
A wallet approvalmethod wallet with wallet_mobile 03XXXXXXXXX (rupees): the sandbox wallet holds the request until the customer approves it in the Bank Studio.202 with status processing; payment.settled once approved. The hosted page waits and updates itself.
An ACH debit and a returnmethod ach_debit with a US routing and account number (dollars); the sandbox bank settles it in its window; a return (R01, R10) can be raised from the Bank Studio.processing, then succeeded; a return after settlement becomes a lost chargeback.
A bank outageOn Payments core, set a rail's sandbox mode to outage.Every call to that rail errors; the router fails over; rail health shows the outage.
A chargebackCard 4000 0000 0000 0259 (tok_dispute): approved, then a chargeback three days after capture that evidence wins; 4000 0000 0000 1976 (tok_dispute_lost) is lost whatever the evidence.A case on Chargebacks with its deadline; the payment's refunded amount follows the outcome.
A webhook that fails and is resentAdd an endpoint whose address answers an error; make a payment; open Webhooks → Deliveries.The delivery retries after 1, 5, 30, 120 and 720 minutes; Resend tries it now; the endpoint's health shows failing.
  • 4242 4242 4242 4242 · Approved (Visa)
  • 5555 5555 5555 4444 · Approved (Mastercard)
  • 4000 0000 0000 3220 · 3-D Secure challenge (code 123456)
  • 4000 0000 0000 0002 · Declined
  • 4000 0000 0000 9995 · Insufficient funds
  • 4000 0000 0000 0119 · Rail error (fails over)
  • 4000 0000 0000 0259 · Approved, then a chargeback
  • 4000 0000 0000 1976 · Approved, then a chargeback that is lost

Changelog

API version 2026-10.

  1. 2026-11-03AddedCheckout sessions and the Pay with Stratos button. POST /api/v1/checkout/sessions opens a hosted checkout page; list, read and expire alongside. Publishable keys (pk_…) open sessions from a web page through POST /api/v1/checkout/embed and the checkout.js script. Endpoints hear checkout.session.completed.
  2. 2026-11-03AddedLimits on every key. Every answer carries x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, x-quota-limit and x-quota-remaining. Over a limit the API answers 429 with Retry-After; from an address outside a key's list, 403 ip_not_allowed.
  3. 2026-11-03AddedWebhook deliveries can be resent. The Webhooks page lists every delivery with its attempts and answers, each endpoint's health over the last week, and Resend.
  4. 2026-10-31AddedUS card routing and ACH debits. method ach_debit with bank_routing_number, bank_account_number, bank_account_name, bank_account_type and sec_code for USD; ACH returns after settlement become lost chargebacks.
  5. 2026-10-29AddedWallet payments and chargebacks from the acquirer. method wallet with wallet_mobile: the payment is processing until the customer approves it on their wallet. Card chargebacks arrive from the acquirer as disputes.
  6. 2026-10-28AddedBank payouts with the bank's title check. POST /api/v1/payouts carries account_title; the sandbox banks answer the title on file.
  7. 2026-10-24AddedThe payments API. Payments (create, read, list, capture, void, refund), payouts (create, read, list) and FX quotes; idempotency keys on every POST; signed webhooks with retries; sandbox keys.

Plugins and SDKs

Install it, or call it from one file.

WooCommerce plugin

Version 1.0.0

A payment gateway for WordPress stores: the customer pays on the hosted checkout, the order is marked paid on the way back or by the signed webhook, and refunds go through the API.

  1. Download the zip and install it under Plugins → Add new → Upload.
  2. In WooCommerce → Settings → Payments → Stratos, choose your desk and paste a secret key (scope payments:write).
  3. Add a webhook endpoint for https://your-store/wc-api/stratos_webhook (checkout.session.completed, payment.refunded) and paste its secret.
  4. Place a sandbox order with the test card 4242 4242 4242 4242.
Download stratos-payments-woocommerce.zipNeeds: A secret API key, A webhook endpoint and its secret.

JavaScript SDK

Version 1.0.0

One file for Node.js 18+: payments, payouts, FX quotes, hosted checkout, sellers and transfers, with an idempotency key on every POST and a webhook signature check.

  1. Download the zip, or copy stratos.mjs into your project.
  2. new Stratos({ key, base }) with a secret key on your server, never in a browser.
  3. Call stratos.checkout.create(...) and send the customer to the url it answers.
  4. Check each webhook with Stratos.verifyWebhook(header, rawBody, secret).
import { Stratos } from "./stratos.mjs";

const stratos = new Stratos({ key: process.env.STRATOS_KEY, base: "https://www.stratosfin.io" });
const session = await stratos.checkout.create({
  amount: 25,
  currency: "USD",
  reference: "ORD-1042",
  success_url: "https://shop.example/thanks",
});
// send the customer to session.url; your endpoint hears checkout.session.completed
Download stratos-sdk-js.zipNeeds: A secret API key.
  • Shopify · later: A payments app goes through Shopify's partner review; until then a store links to a hosted checkout from its order confirmation.
  • Magento (Adobe Commerce) · later: A payment method module for the Marketplace review; the API calls are the same as the WooCommerce plugin's.

Webhooks

Signed events, checked in a few lines.

Events

  • payment.authorizedA manual-capture payment is waiting to be captured.
  • payment.settledA payment succeeded.
  • payment.under_reviewA payment is held for a person to review.
  • payment.failedA payment was declined, failed or voided.
  • checkout.session.completedA hosted checkout was paid (its payment events arrive too).
  • payment.refundedA payment was refunded in full or in part.
  • transfer.createdA seller's share of a payment was recorded (a split or a transfer).
  • transfer.paidA seller was paid their share.
  • transfer.reversedA seller's share was reversed before it was paid.
  • subscription.createdA customer subscribed to a plan.
  • subscription.paidA subscription's period was charged.
  • subscription.past_dueA subscription's charge did not go through; it will be tried again.
  • subscription.cancelledA subscription ended, by request or after the last retry.
  • invoice.sentAn invoice was sent to its customer with the pay link.
  • invoice.payment_recordedA payment landed on an invoice, through the pay link or recorded by hand.
  • invoice.paidAn invoice is paid in full.
  • invoice.overdueAn invoice is a week past its due date and still open.
  • invoice.voidedA sent invoice was voided.
  • payout.createdA payout instruction was recorded.
  • payout.paidA payout was confirmed as paid.
  • payout.failedA payout did not go through.
  • payout.returnedA paid payout came back from the receiving bank.

Headers

  • Stratos-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" with your endpoint's secret>
  • Stratos-EventThe event type, for example payment.settled.
  • Stratos-DeliveryThe delivery id; the same id on every retry, so you can skip repeats.

Check the signature

const crypto = require("node:crypto");

// header: the Stratos-Signature header; rawBody: the body exactly as received (a string).
function verifyStratosSignature(header, rawBody, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1, "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

module.exports = { verifyStratosSignature };
  • Read the raw body before parsing it: the signature covers the exact bytes sent.
  • Refuse a timestamp more than five minutes away; it stops a captured delivery being replayed.
  • Compare signatures in constant time.
  • Answer 2xx within eight seconds. Anything else is retried with back-off for about 15 hours.
  • Only public https addresses receive webhooks.

Fin in Claude

Ask your business from Claude.

  1. 1

    Make a Fin token

    In the control panel, open Fin access and make a token (read only, or able to propose changes you confirm). It acts as you and is shown once.

  2. 2

    Keep it in your shell

    Set it as an environment variable; never paste it into a file you commit.

    export STRATOS_FIN_TOKEN=fin_…
    export STRATOS_FIN_URL=https://www.stratosfin.io
  3. 3

    Add Fin to Claude Code

    One command adds the MCP server for your projects.

    claude mcp add --transport http stratos-fin https://www.stratosfin.io/api/mcp \
      --header "Authorization: Bearer $STRATOS_FIN_TOKEN"
  4. 4

    Or use a config file

    For a project's .mcp.json, or Claude Desktop's connector settings:

    {
      "mcpServers": {
        "stratos-fin": {
          "type": "http",
          "url": "https://www.stratosfin.io/api/mcp",
          "headers": {
            "Authorization": "Bearer ${STRATOS_FIN_TOKEN}"
          }
        }
      }
    }
  5. 5

    Ask

    Fin answers with your own permissions. Changes come back as proposals you confirm on the Fin screen; nothing moves money.

    Using Fin, how is today going compared with last week?
    Ask Fin for last month's statement and summarise the largest categories.

Status

Live now, and after licensing.

Live in the control panel

  • Payments API in the sandbox (OpenAPI 3.1, Postman)
  • Merchant, staff and customer dashboards
  • API key management (sandbox)
  • Hosted KYC (document and face)
  • Payment links, settlements and disputes
  • iOS and Android app on the same ledger

After licensing

  • Production acquiring keys
  • Public SDK downloads
  • App Store and Play listings
TLS 1.3
Tokenised cards
3DS2 and PCI-aligned

Engineering desk

Who you'll work with.

Mughees Ahmad
Founder, CTO & Director
Shehroz Virk
Product Lead

Get sandbox access.

Tell us what you're building and we'll set you up.