SIMWAY
Partners

Account, balance and statement

Seven endpoints that answer questions about you rather than about what we sell: are we up, who is this key, how much money is left, where did it go, and how to put more in.

Health, without a credential

GET /health is the only unauthenticated route on this API and the only one subject to no rate limit at all. It exists so your monitoring can watch us without you putting a credential into a monitoring system.

It answers 503 — not a 200 with a sad body — when the database probe fails or exceeds 2 seconds. An uptime checker configured with nothing but a URL therefore does the right thing.

Balance

  • Prepaid. Every purchase debits it and it can never go below zero — a purchase that would take it under is refused with a 402 carrying your balance and the amount required.
  • Read fresh on every call, deliberately un-cached, because it is the number a checkout decides on.
  • discount_bps here is your headline rate — the tier, or the override that supersedes it. The rate on any given plan can be lower, and the catalogue row is where you read that. Why they differ.
  • A sandbox key reports the sandbox float in the same field. Same shape, imaginary money.

The statement

Every movement is a row, newest first, and each row carries the balance it produced. The sequence number and that balance are allocated by the same statement that moved the money, which is why any window reconciles without a second query:

True of any page, by construction
sum(amount_cents)
  == balance_after_cents(last) - balance_after_cents(first) + amount_cents(first)
kindSignRaised by
depositpositiveA deposit that was credited — a crypto invoice you paid, or a bank transfer we recorded.
purchasenegativeAn accepted order.
refundpositiveUndelivered units on a failed or partial order. One row per order, at most.
adjustmenteitherA manual correction.
payoutnegativeA withdrawal.
chargebacknegativeA reversal.

created_after on this endpoint is inclusive. On /orders and /esims the same parameter is exclusive. That inconsistency is real, it is not going to be fixed silently, and it is written here so it does not become an off-by-one in your reconciliation.

Deposits

Three routes. POST /deposits opens a cryptocurrency invoice, GET /deposits is your funding history, and GET /deposits/{id}is the answer to “I lost the invoice link”. This is the only rail on the API that makes the balance go up, and the only one whose effect you cannot undo.

POST /deposits answers 201, not the order endpoint’s 202, and the difference is the point: a 202 there means “we have your money and are provisioning”, while here nothing is in flight. What comes back is an invoice. The deposit is pending, your float is untouched, and the credit happens when the cryptocurrency settles and our payment provider calls us back. Amounts are whole dollars, $100 to $10,000 — the ceiling bounds one fat-finger rather than how much float you may hold, and two invoices are always available for more.

It is also the one write on this surface that takes no Idempotency-Key. A repeated POST mints a second invoice, which costs a row and nothing else: an unpaid invoice is not a charge, and two invoices credit twice only if you pay both — at which point you have sent twice the money and both credits are correct. Where the key does matter.

Four things about the money, and three of them surprise people

  • An overpayment is credited in full. Not kept, and not capped at the invoice. A $500 invoice that settles as slightly more in USD puts the whole of it on your float, so credited_cents greater than requested_cents is an ordinary response and not an error. Reconcile against credited_cents; the invoiced figure is what you asked for, not what you have.
  • An underpayment is credited at what arrived. A short settlement credits the short amount — not nothing, and not the invoice. While the provider still considers the invoice open and payable, nothing is credited at all: the deposit stays pending until the amount is final. A settlement that reports a readable zero credits nothing and raises a review on our side.
  • A refund before the credit is terminal. A refund after it does not take the float back. Money returned before we credited it closes the deposit as refunded, and nothing will ever re-drive it — unlike cancelled, which stays claimable because crypto broadcast before an invoice lapsed and confirmed after it is ordinary. Money returned after we credited it does not reverse automatically: the float stays where it is, possibly already spent on profiles, and a person reconciles it. That is the one behaviour here worth knowing before it happens.
  • An account closed between the invoice and the payment is never credited. No float is opened on a closed account, the deposit stays claimable, and somebody has to decide between returning the money and re-opening the account. Do not open an invoice you are not about to pay.

A credited deposit writes a deposit row on the statement, and ledger_entry_id on the deposit is the join back to it. What deposits look like when you go live.

Endpoints

GET/health

Service health

No key requiredNot rate limited

The only route on this API that takes no key and is subject to no rate limit. It exists so your monitoring can watch us without you embedding a credential in a monitoring system.

It answers 503 — not 200 with a sad body — when the database probe fails or takes longer than two seconds, so an uptime checker configured with nothing but a URL still does the right thing.

Example

Request
curl 'https://api.simway.pro/partner/v1/health'
200 — Healthy
{
  "status": "ok",
  "uptime_seconds": 41233,
  "database": "ok"
}
503 — Database unreachable or slow
{
  "status": "degraded",
  "uptime_seconds": 41233,
  "database": "down"
}
GET/me

Your account and this key

Requires a keyRate class read

Read fresh from the database on every call, never from the cached principal — so it is the authoritative answer to “which key is this and is it live”.

mode is a property of the KEY, never of the account and never of the request. No header, query flag or body field can change it.

rate_limits is computed live from your band. It is the machine-readable form of the rate-limit page and it moves with your tier without us redeploying anything, so read it rather than hard-coding the table.

Example

Request
curl 'https://api.simway.pro/partner/v1/me' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000'
200 — The account
{
  "object": "partner",
  "id": "pt_c8k2m4p9x1v7b3n6q0w5t2r8",
  "name": "Northwind Travel",
  "slug": "northwind-travel",
  "status": "live",
  "mode": "live",
  "tier": "silver",
  "key": {
    "id": "pk_c4h9j2n7x5v1b8m3q6w0t4r2",
    "label": "production-checkout",
    "last4": "aB3x",
    "mode": "live",
    "expires_at": null,
    "created_at": "2026-07-02T11:20:41Z"
  },
  "rate_limits": {
    "read": {
      "limit": 20,
      "window_seconds": 1
    },
    "write": {
      "limit": 25,
      "window_seconds": 5
    },
    "supplier": {
      "limit": 2,
      "window_seconds": 1
    },
    "catalogue": {
      "limit": 120,
      "window_seconds": 60
    }
  },
  "created_at": "2026-06-18T08:02:15Z"
}

Errors it can return

GET/balance

Balance

Requires a keyRate class read

Deliberately un-cached: read fresh on every call, because it is the number a checkout decides on.

A sandbox key reports the sandbox float instead. Same field, same shape — only the money is imaginary.

Example

Request
curl 'https://api.simway.pro/partner/v1/balance' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000'
200 — Balance
{
  "object": "balance",
  "balance_cents": 48309,
  "currency": "USD",
  "tier": "silver",
  "discount_bps": 1200,
  "low_balance_threshold_cents": 2000,
  "mode": "live"
}

Errors it can return

GET/ledger

Statement

Requires a keyRate class read

The statement is the books. seq and balance_after_cents are allocated by the same statement that moved the money, so any window reconciles by construction: the sum of amount_cents over a page equals the difference of the balances at its ends.

A sandbox key always receives an empty page. Sandbox spend moves the sandbox float and writes no statement row at all, because the ledger has no mode column and a sandbox row would break the invariant an hourly job checks. The practical consequence is worth planning for: you cannot exercise statement-rendering code in sandbox.

Query parameters

NameTypeRules
kindenumFilter by movement type.One of: deposit, purchase, refund, adjustment, payout, chargeback
created_afterISO 8601INCLUSIVE — created_at >= value. Note this differs from /orders and /esims, where the same parameter is exclusive.
created_beforeISO 8601Exclusive — created_at < value.
limitintegerPage size.1–200Default 50
cursorstringOpaque. Pass back next_cursor from the previous page, or omit it to start again. A malformed cursor is a 400 naming cursor, never a silent first page.≤ 512 characters

Example

Request
curl 'https://api.simway.pro/partner/v1/ledger?kind=purchase' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000'
200 — One page
{
  "data": [
    {
      "object": "ledger_entry",
      "id": "led_c9x4k7n2v5b1m8q3w0t6r4j9",
      "seq": 4412,
      "kind": "purchase",
      "amount_cents": -5256,
      "balance_after_cents": 43053,
      "description": "Example Republic 5 GB / 30 days x3",
      "order_id": "po_c7m2k9x4v1b6n3q8w5t0r2j7",
      "deposit_id": null,
      "created_at": "2026-08-17T09:14:22Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJzIjo0NDEyfQ"
}

Errors it can return

POST/deposits

Open a deposit

Requires a keyRate class write

The only route that makes the balance go UP, and the only one whose answer is not something that has happened. It creates an INVOICE: the deposit is pending, your float is untouched, and a deposit you open and walk away from has cost you a row and nothing else. The credit happens when the crypto settles and the provider calls us back, and only then.

It answers 201, not the orders endpoint's 202. A 202 there means "we have your money and are provisioning". Here nothing is in flight and nothing has been accepted — a resource was created and the money arrives if and when you pay it.

Whole dollars only, $100 to $10,000. The ceiling bounds one fat-finger, not how much float you may hold: two invoices are always available, and a bank transfer has a person on it and no ceiling at all.

Crypto, not cards. That is a decision rather than a gap, and the reason is on the page that renders this endpoint.

Body

NameTypeRules
amount_cents *integerInteger US cents, and a whole number of dollars — a multiple of 100. It is the amount you are choosing to send, not an amount you are reporting, and a round figure is what makes an invoice, a bank statement and a ledger row line up when somebody reconciles them by eye later. What actually arrives is credited to the cent regardless.$100–$10,000, in multiples of 100 cents

Example

Request
curl -X POST 'https://api.simway.pro/partner/v1/deposits' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount_cents": 50000
}'
201 — Invoice created — nothing has moved yet
{
  "object": "deposit",
  "id": "dep_c5n2k8x7v3b9m1q4w6t0r7j2",
  "status": "pending",
  "method": "crypto",
  "requested_cents": 50000,
  "credited_cents": null,
  "currency": "USD",
  "reference": null,
  "ledger_entry_id": null,
  "payment_url": "https://pay.example.com/invoice/9f2c41b8e7d54a0c",
  "expires_at": "2026-08-17T10:14:22Z",
  "created_at": "2026-08-17T09:14:22Z",
  "credited_at": null
}

Errors it can return

GET/deposits

List deposits

Requires a keyRate class read

Every deposit on the account, whichever rail it came in on: the crypto invoices this API mints, and the bank transfers a person records for you. method tells them apart.

The invoice link is on the list as well as on the single read, because "which of my deposits is still waiting to be paid, and where do I pay it" is the question this endpoint exists to answer.

A SANDBOX key always receives an empty page. That is consistent rather than missing: the sandbox float is not money, so it writes no deposit and no statement row — the same answer GET /ledger gives, for the same reason.

Query parameters

NameTypeRules
statusenumFilter by state. refunded is terminal. cancelled is NOT: an invoice that lapsed is still claimable, because crypto broadcast before it lapsed and confirmed after it is ordinary.One of: pending, credited, cancelled, refunded, reversed
limitintegerPage size.1–200Default 50
cursorstringOpaque. Pass back next_cursor from the previous page, or omit it to start again. A malformed cursor is a 400 naming cursor, never a silent first page.≤ 512 characters

Example

Request
curl 'https://api.simway.pro/partner/v1/deposits?status=credited' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000'
200 — One page — an open invoice, an overpaid credit, and a bank transfer
{
  "data": [
    {
      "object": "deposit",
      "id": "dep_c5n2k8x7v3b9m1q4w6t0r7j2",
      "status": "pending",
      "method": "crypto",
      "requested_cents": 50000,
      "credited_cents": null,
      "currency": "USD",
      "reference": null,
      "ledger_entry_id": null,
      "payment_url": "https://pay.example.com/invoice/9f2c41b8e7d54a0c",
      "expires_at": "2026-08-17T10:14:22Z",
      "created_at": "2026-08-17T09:14:22Z",
      "credited_at": null
    },
    {
      "object": "deposit",
      "id": "dep_c2v8k4n7x1b5m9q3w6t0r8j4",
      "status": "credited",
      "method": "crypto",
      "requested_cents": 50000,
      "credited_cents": 50200,
      "currency": "USD",
      "reference": "5b0e7a94-2d13-4f8c-9a26-0c7e1f45b382",
      "ledger_entry_id": "led_c9x4k7n2v5b1m8q3w0t6r4j9",
      "payment_url": "https://pay.example.com/invoice/4c1f8a26d90b7e35",
      "expires_at": "2026-08-11T13:40:08Z",
      "created_at": "2026-08-11T12:40:08Z",
      "credited_at": "2026-08-11T12:52:44Z"
    },
    {
      "object": "deposit",
      "id": "dep_c7b3k1n9x4v6m2q8w5t0r3j6",
      "status": "credited",
      "method": "wire",
      "requested_cents": 3000000,
      "credited_cents": 3000000,
      "currency": "USD",
      "reference": "FT-XX-9042175538",
      "ledger_entry_id": "led_c4k8n1x6v3b7m2q9w5t0r8j3",
      "payment_url": null,
      "expires_at": null,
      "created_at": "2026-08-04T15:31:12Z",
      "credited_at": "2026-08-04T15:31:12Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJjIjoiMjAyNi0wOC0wNFQxNTozMToxMi4wMDBaIiwiaSI6ImM3YjNrMW45eDR2Nm0ycTh3NXQwcjNqNiJ9"
}

Errors it can return

GET/deposits/{id}

Get one deposit

Requires a keyRate class read

The answer to "I lost the invoice link". Same shape as a row in the list, always scoped to your own account.

A SANDBOX key never resolves a deposit here: the sandbox writes none, so every well-formed dep_ id is 404 not_found. It is not "404 whatever you send" — the id is parsed first, so a wrong-kind id is the 400 described below and mode is never reached.

Path parameters

NameTypeRules
id *stringMust begin dep_. A wrong-kind id is a 400 naming id, not a 404.

Example

Request
curl 'https://api.simway.pro/partner/v1/deposits/dep_c5n2k8x7v3b9m1q4w6t0r7j2' \
  -H 'Authorization: Bearer simway_sk_test_ExampleKeyDoNotUse0000000000000000000000000'
200 — An invoice waiting to be paid
{
  "object": "deposit",
  "id": "dep_c5n2k8x7v3b9m1q4w6t0r7j2",
  "status": "pending",
  "method": "crypto",
  "requested_cents": 50000,
  "credited_cents": null,
  "currency": "USD",
  "reference": null,
  "ledger_entry_id": null,
  "payment_url": "https://pay.example.com/invoice/9f2c41b8e7d54a0c",
  "expires_at": "2026-08-17T10:14:22Z",
  "created_at": "2026-08-17T09:14:22Z",
  "credited_at": null
}

Errors it can return