Fortion API documentation
Add hosted pay-ins and controlled payout requests to your server. Fortion keeps provider credentials, routing and payment status inside one orchestration layer.
https://fortionps.comBearer integration tokenapplication/jsonThe API creates a payout request for review. Fortion does not automatically transfer funds, hold a merchant balance or create an internal ledger entry in the current version.
Connect a merchant in five steps
Use one shop for one merchant payment context. The shop owns its routes, integration tokens and webhook secret, so credentials never need to cross company boundaries.
-
1
Prepare the shop
Ask Fortion to activate the company, shop and at least one payment route.
-
2
Create server credentials
In Merchant Admin open the shop → Integration. Add a public HTTPS endpoint, create a webhook secret, then create a server key with “Pay-ins and payouts” permissions.
-
3
Create a payment
Send one server request and redirect the customer to the returned
checkoutUrl. -
4
Confirm the result
Verify the signed webhook. A browser redirect is useful for UX, but is not proof of payment.
-
5
Create payout requests
Send safe recipient references only. Track review and execution status from your server.
Keep both secrets on your server
The integration token authorizes API calls. The webhook secret verifies events sent by Fortion. Both are displayed only once and must remain server-side only.
Your frontend calls your own backend. Your backend calls Fortion with the
Authorization: Bearer … header.
Authorization: Bearer sdk.sit_xxx.your_secret
Content-Type: application/json
Permissions
orders:createCreate and start a pay-inorders:readRead pay-in statuspayouts:createCreate a payout requestpayouts:readRead payout status/sdk/orders
Create and start a payment
The shortest valid request has three business fields. If the shop has one default route,
paymentSelection is not required.
curl https://fortionps.com/sdk/orders \
-X POST \
-H "Authorization: Bearer $FORTION_INTEGRATION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "order_1001",
"productName": "Pro plan",
"amount": 199
}'
Response
{
"order": {
"id": "ord_abc123",
"externalOrderId": "order_1001",
"status": "in_progress"
},
"checkoutUrl": "https://fortionps.com/pay/random-checkout-token",
"reusedExistingOrder": false
}
The current pay-in request does not select a currency per order. Confirm the shop currency before launch.
/sdk/orders/by-external/:externalOrderId
Read a payment by your identifier
Store your externalOrderId and use it for recovery or support. Webhooks remain the preferred
source of terminal status.
curl "https://fortionps.com/sdk/orders/by-external/order_1001" \
-H "Authorization: Bearer $FORTION_INTEGRATION_TOKEN"
createdOrder exists, payment has not started
in_progressCustomer or provider action is pending
completedPayment completed
failedPayment failed
/sdk/available-payment-routes
List available payment routes
Call this only when your product lets a customer select a method. Otherwise omit
paymentSelection and let the shop default route decide.
/sdk/payouts
Create a payout request
This endpoint records merchant intent and returns pending_review. It does not execute a bank,
wallet or crypto transfer automatically.
curl https://fortionps.com/sdk/payouts \
-X POST \
-H "Authorization: Bearer $FORTION_INTEGRATION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalPayoutId": "payout_1001",
"amount": 125000,
"currency": "UZS",
"country": "UZ",
"methodType": "bank_transfer",
"recipient": {
"reference": "recipient_42",
"displayName": "Example LLC",
"maskedDestination": "****1234"
}
}'
Card numbers, CVV, full bank account numbers and IBAN values are rejected. Send your internal recipient
reference and an already-masked destination only. Use opaque alphanumeric identifiers such as
payout_1001; any field containing 12 or more consecutive digits is rejected as potentially
sensitive destination data.
Current country and currency pairs
UZSKGS/sdk/payouts/by-external/:externalPayoutId
Read payout status
Use your externalPayoutId for reconciliation. The response separates request review from
execution state so a reviewed request is never confused with moved funds.
pending_reviewWaiting for an operator decision
approvedApproved for controlled execution
rejectedRejected with a safe reason code when available
completedManual execution was recorded as succeeded
Trust signed webhooks, not redirects
Fortion sends JSON events to the shop’s public HTTPS endpoint. Return 2xx only after your
system persists the event. Failed deliveries are retried from a durable queue.
payment.completedA pay-in reached terminal successpayment.failedA pay-in reached terminal failurepayout.updatedA payout review or execution state changedsubscription.updatedSubscription compatibility eventHeaders
x-fortion-event-id: mwe_123
x-fortion-event-type: payment.completed
x-fortion-signature-timestamp: 2026-07-28T12:00:00.000Z
x-fortion-signature: v1=<hex>
Verify HMAC SHA-256 on the raw body
Build the signed value as ${timestamp}.${rawBody}. Reject timestamps older than five minutes
and process every event idempotently by event ID.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyFortionWebhook({ rawBody, timestamp, signature, secret }) {
if (!signature?.startsWith("v1=")) return false;
const age = Math.abs(Date.now() - Date.parse(timestamp));
if (!Number.isFinite(age) || age > 5 * 60 * 1000) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`, "utf8")
.digest("hex");
const received = signature.slice(3);
const left = Buffer.from(received, "hex");
const right = Buffer.from(expected, "hex");
return left.length === right.length && timingSafeEqual(left, right);
}
Retry with the same merchant identifier
For both pay-ins and payouts, the same identifier and the same payload return the existing resource. The
same identifier with a different payload returns 409 Conflict.
externalOrderId
Unique inside one shop. Reuses the same order and active payment session.
externalPayoutId
Unique inside one shop. Reuses the same payout request.
Handle errors by HTTP status
400Invalid request. Fix the fields before retrying.401Missing, invalid or revoked token.403The token does not have the required permission.404Resource is not visible inside this shop.409Route conflict or reused identifier with a different payload.Run the reference merchant locally
The repository includes a small merchant site that calls the same pay-in and payout endpoints from its backend. Copy the environment template, add a non-production token, then run:
cd examples/merchant-quickstart
cp .env.example .env
npm start
Fortion accepts only a public HTTPS endpoint, not localhost or a private IP. Test the handler locally with the signed fixture, then use a controlled public test endpoint for delivery testing.
API reference
Before you go live
- Use a separate token for each shop and environment.
- Confirm the payment route, provider account and shop currency with Fortion.
- Store the integration token and webhook secret in secret storage.
- Verify webhook timestamp, signature and event ID before updating state.
- Test a completed pay-in, a failed pay-in and an idempotent retry.
- Confirm payout permissions and the manual review process with operators.