API reference

Forypay REST API

Plain JSON over HTTPS with bearer authentication. Two endpoints cover the whole payment lifecycle; a third verifies a single transaction you already have a hash for.

Authentication

Every request carries a project API key as a bearer token. Keys are created in the dashboard, on the project they belong to — see Projects — are shown once at creation, and are stored only as a hash — if you lose one, revoke it and issue another.

Header
Authorization: Bearer fp_live_9f2c…

Keys are secrets

Call the API from your server, never from browser or mobile code. A key in a client bundle can create payments against your project.

Create a payment

POST/api/v1/payments

Registers the payment you expect to receive and starts chain monitoring. The response tells you when the window closes.

Try it
Request body fields for creating a payment
FieldTypeDescription
networkrequired"bsc" | "tron"Chain the payer will send on.
receiverrequiredstringYour receiving address. 0x… on BSC, T… on TRON.
amountrequiredstringDecimal amount as a string, e.g. "25.00". Sent as a string to avoid float rounding.
order_idoptionalstringYour own reference, up to 191 characters. Echoed back on every webhook.
expires_inoptionalintegerSeconds until the payment window closes. Minimum 60. Omit for no expiry.
expected_senderoptionalstringRestricts matching to transfers from this address.

Pass an Idempotency-Key header to make retries safe: a repeat call with the same key returns the original payment instead of creating a second one.

Request
curl -X POST https://your-host/api/v1/payments \
  -H "Authorization: Bearer $FP_API_KEY" \
  -H "Idempotency-Key: ORDER-1042" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "ORDER-1042",
    "network": "bsc",
    "receiver": "0x2f1a…9c3e",
    "amount": "25.00",
    "expires_in": 1800
  }'
200 Response
{
  "id": "pay_8Fq2mZx1LrKd",
  "order_id": "ORDER-1042",
  "network": "bsc",
  "token": "USDT",
  "amount": "25.00",
  "receiver": "0x2f1a…9c3e",
  "status": "PENDING",
  "expires_at": "2026-08-17T14:35:00.000Z"
}

Retrieve a payment

GET/api/v1/payments/{id}

Returns the current state plus every matched transfer. Use this to reconcile, or as a fallback if your webhook endpoint was unreachable.

Try it
Request
curl https://your-host/api/v1/payments/pay_8Fq2mZx1LrKd \
  -H "Authorization: Bearer $FP_API_KEY"
200 Response
{
  "id": "pay_8Fq2mZx1LrKd",
  "order_id": "ORDER-1042",
  "network": "bsc",
  "token": "USDT",
  "expected_amount": "25.00",
  "detected_amount": "25.00",
  "status": "CONFIRMED",
  "transaction_hash": "0x9d4c…7b21",
  "sender": null,
  "receiver": "0x2f1a…9c3e",
  "created_at": "2026-08-17T14:05:00.000Z",
  "detected_at": "2026-08-17T14:05:12.000Z",
  "confirmed_at": "2026-08-17T14:05:21.000Z",
  "transfers": [ /* … */ ]
}

List payments

GET/api/v1/payments

Returns recent payments, newest first, wrapped in a data array. Pass limit to change the page size — the default is 20 and the maximum is 100.

Try it
Request
curl "https://your-host/api/v1/payments?limit=50" \
  -H "Authorization: Bearer $FP_API_KEY"

Verify a transaction

POST/api/v1/verify

A one-shot check for a hash you already have. This reads the chain directly and does not create or update a payment record — useful for manual reconciliation and for migrating historical orders.

Try it
Request body fields for verifying a transaction
FieldTypeDescription
networkrequired"bsc" | "tron"Chain to read.
transaction_hashrequiredstringHash of the transfer to inspect.
receiverrequiredstringAddress the transfer must have credited.
Request
curl -X POST https://your-host/api/v1/verify \
  -H "Authorization: Bearer $FP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "network": "bsc",
    "transaction_hash": "0x9d4c…7b21",
    "receiver": "0x2f1a…9c3e"
  }'
200 Response
{
  "valid": true,
  "status": "CONFIRMED",
  "network": "bsc",
  "transaction_hash": "0x9d4c…7b21",
  "sender": "0x8f2a…1b4e",
  "receiver": "0x2f1a…9c3e",
  "amount": "25.00"
}

Payment statuses

A payment starts at PENDING and moves forward only. These are the states you will see in webhooks, API responses and the dashboard.

Payment status values and what they mean
StatusMeaning
PendingCreated and waiting for a matching transfer on chain.
DetectedA matching transfer was found; the payment settles immediately.
ConfirmedSettled. Safe to fulfil the order.
OverpaidSettled, but the payer sent more than the expected amount.
ExpiredThe window closed before a matching transfer arrived.

Webhooks

Set a webhook URL on the project and we POST JSON as the payment progresses. Events are queued and retried with exponential backoff until your endpoint returns a 2xx, so treat handlers as idempotent — the same event may arrive twice.

  • payment.detected — a matching transfer appeared on chain and was settled
  • payment.confirmed — the payment moved to its final settled state
  • payment.expired — the window closed unpaid

Each delivery carries an HMAC-SHA256 signature derived from the project webhook secret, computed over the timestamp header and the raw request body joined by a dot. Compare it with a constant-time comparison before trusting the payload, refuse anything older than five minutes, and rotate the secret from project settings if it is ever exposed.

Example delivery
POST /your/webhook
Content-Type: application/json
X-Forypay-Event: payment.confirmed
X-Forypay-Delivery: whk_3Kd9vQ2mTz
X-Forypay-Timestamp: 1755820800
X-Forypay-Signature: sha256=6f1b…a92c

{
  "event": "payment.confirmed",
  "data": {
    "payment_id": "pay_8Fq2mZx1LrKd",
    "order_id": "ORDER-1042",
    "status": "CONFIRMED",
    "transaction_hash": "0x9d4c…7b21"
  }
}

Errors

Errors use conventional HTTP status codes and a consistent body. The request_id is worth logging — quote it when asking for help and we can find the exact request.

400 Response
{
  "error": {
    "code": "INVALID_AMOUNT",
    "message": "Invalid amount",
    "request_id": "req_7bQ1xKp0Za"
  }
}
Error codes returned by the API
CodeHTTPCause
INVALID_REQUEST400Body failed schema validation. The message names the field.
INVALID_AMOUNT400Amount was not a plain decimal string.
INVALID_TOKEN400Token is not enabled for that network.
INVALID_API_KEY401Missing, malformed, revoked or unknown bearer token.
PAYMENT_NOT_FOUND404No payment with that id.
UNKNOWN_TOKEN_DECIMALS500Token metadata is incomplete on our side.
VERIFICATION_ERROR500The chain read failed — retry, then contact us.

Try it without writing code

The playground sends a real request to any documented endpoint with a key you paste in, and shows the raw response.

Open API playground