External API — Partner Integration Guide
Create invoices, query status, share payment links, and receive signed webhooks.
| Environment | Base URL |
|---|---|
| Production | https://integration.rsompay.com/api/external/v1 |
| Sandbox | https://integration-demo.rsompay.com/api/external/v1 |
| Environment | Payment URL pattern |
|---|---|
| Production | https://payment.rsompay.com/pay/invoice/{token} |
| Sandbox | https://payment-demo.rsompay.com/pay/invoice/{token} |
| Item | Description |
|---|---|
| External client account | Organization with scope = external |
| Integration API token | Sanctum token client-external-api |
| Webhook signing secret | Per integration partner — only this value is the HMAC key for X-Rsom-Signature (see §7.5) |
flowchart TB
subgraph partner [Partner system]
ERP[ERP backend]
WH[Webhook endpoint]
RET[return_url handler]
end
subgraph rsom [RsomPay]
INT[integration.rsompay.com]
PAY[payment.rsompay.com]
end
ERP -->|POST /invoices| INT
INT -->|payment_url| ERP
ERP -->|share link| PAY
PAY -->|browser redirect| RET
INT -->|POST notification_url| WH
ERP -->|GET by-reference| INT
Send the integration token on every request:
Authorization: Bearer YOUR_CLIENT_EXTERNAL_API_TOKEN Content-Type: application/json Accept: application/json
client-external-api.403.client_invoices.create, client_invoices.view.curl -X POST "https://integration.rsompay.com/api/external/v1/invoices" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d @invoice.json
Successful responses:
{
"status": true,
"message": "Mission completed successfully",
"data": { }
}
Paginated GET /transactions adds meta: total, per_page, current_page, last_page.
external_id and reference_number only.
POST /invoices
| Field | Required | Type | Description |
|---|---|---|---|
external_id | Yes | string | Partner idempotency key. Max 255. Unique per client; duplicates return the same invoice. |
notification_url | Yes | URL | HTTPS webhook endpoint. Publicly reachable; no localhost/private IPs. Max 2048. |
return_url | Yes | URL | HTTPS customer redirect after checkout. Max 2048. |
customer | Yes | object | Customer block — see table below. |
items | Yes | array | Line items — min 1 object; see table below. |
currency_code | No | string | Currency code. Max 8. Default SAR. |
due_date | No | date | Due date YYYY-MM-DD. |
issued_date | No | date | Issue date YYYY-MM-DD. |
notes | No | string | Invoice notes. Max 2000. |
customer)| Field | Required | Type | Description |
|---|---|---|---|
customer.fullname | Yes | string | Full name on payment page. Max 255. |
customer.id | No | string | Your customer id. Max 255. Auto CUST-{uuid} if omitted. |
customer.email | No | string | Valid email. Max 255. |
customer.phone | No | string | Phone (e.g. +966501234567). Max 32. |
items[])Each array element is one line item. At least one item is required.
| Field | Required | Type | Description |
|---|---|---|---|
items[].description | Yes | string | Line description. Max 255. |
items[].quantity | No | number | Qty. Min 0.001. Default 1. |
items[].unit_price | Yes | number | Unit price before discount and tax. Min 0.01. Invoice total is computed from all lines. |
items[].discount_amount | No | number | Line discount amount. Min 0. |
items[].tax_rate | No | number | Tax % (e.g. 15 = 15% VAT). |
items[].item_type | No | string | Category label (e.g. service). Max 32. |
items[].reference_id | No | string | Your line reference. Max 128. |
external_id → 201, same invoice, no duplicate.issued.amount field — each line requires unit_price. data.amount / data.balance = sum of lines (must be > 0).One line: qty 1, unit 100, discount 10, tax 15% → grand_total: 103.5
{
"external_id": "INV-PARTNER-001",
"notification_url": "https://partner.example.com/webhooks/rsom",
"return_url": "https://partner.example.com/payment/return",
"currency_code": "SAR",
"due_date": "2026-12-31",
"issued_date": "2026-05-18",
"notes": "Optional invoice note",
"customer": {
"id": "CUST-9",
"fullname": "Ahmed Ali",
"email": "customer@example.com",
"phone": "+966501234567"
},
"items": [
{
"description": "Service fee",
"item_type": "service",
"quantity": 1,
"unit_price": 100,
"discount_amount": 10,
"tax_rate": 15,
"reference_id": "LINE-1"
}
]
}
{
"status": true,
"message": "Mission completed successfully",
"data": {
"external_id": "INV-PARTNER-001",
"reference_number": "INV-42-001",
"status": "issued",
"amount": 103.5,
"balance": 103.5,
"payment_url": "https://payment.rsompay.com/pay/invoice/...",
"customer": {
"id": "CUST-9",
"fullname": "Ahmed Ali",
"email": "customer@example.com",
"phone": "+966501234567"
},
"totals": {
"subtotal": 100,
"discount_total": 10,
"tax_total": 13.5,
"grand_total": 103.5
},
"items": [
{
"description": "Service fee",
"item_type": "service",
"quantity": 1,
"unit_price": 100,
"discount_amount": 10,
"tax_rate": 15,
"tax_amount": 13.5,
"total_amount": 103.5,
"reference_id": "LINE-1"
}
],
"latest_payment": null
}
}
GET /invoices/by-reference?{parameter}={value}
Provide exactly one query parameter:
| Parameter | Description |
|---|---|
external_id | Your idempotency key (recommended) |
reference_number | RsomPay invoice number (e.g. INV-42-001) |
GET /invoices/by-reference?external_id=INV-PARTNER-001 GET /invoices/by-reference?reference_number=INV-42-001
List customer payments across external API invoices. Shows the paid amount only — not fees, net settlement, or settlement status.
GET /transactions?customer_id={id}&page=1&per_page=20
| Parameter | Required | Default | Max |
|---|---|---|---|
customer_id | Yes | — | 255 |
page | No | 1 | — |
per_page | No | 20 | 100 |
data)| Field | Description |
|---|---|
reference | Payment reference |
status | completed, pending, failed, etc. |
amount | Amount the customer paid |
currency_code | e.g. SAR |
gateway | Payment gateway |
invoice_external_id | Your invoice external_id |
captured_at | When payment was captured (if available) |
created_at | Record created at |
RsomPay POSTs to your notification_url after payment activity. No webhook is sent when the invoice is created. Delivery is asynchronous — respond with HTTP 2xx quickly.
| Event | When |
|---|---|
invoice.status_changed | Status changes after payment (previous_status in payload) |
payment.completed | Payment succeeded |
payment.failed | Payment failed |
POST {notification_url}
Content-Type: application/json
X-Rsom-Event: payment.completed
X-Rsom-Event-Id: 550e8400-e29b-41d4-a716-446655440000
X-Rsom-Timestamp: 1716033600
X-Rsom-Signature: a1b2c3d4e5f6...
{
"event": "payment.completed",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"occurred_at": "2026-05-18T12:00:00+00:00",
"customer": { "id": "CUST-9", "fullname": "Ahmed Ali", ... },
"totals": { "subtotal": 100, "discount_total": 10, "tax_total": 13.5, "grand_total": 103.5 },
"invoice": {
"external_id": "INV-PARTNER-001",
"reference_number": "INV-42-001",
"status": "paid",
"amount": 103.5,
"balance": 0,
"payment_url": "https://payment.rsompay.com/pay/invoice/..."
},
"payment": {
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"gateway": "dhamen",
"status": "completed",
"amount": 103.5,
"paid_at": "2026-05-18 12:00:00"
}
}
2xx within a few seconds.event_id.RsomPay sends the webhook as an HTTP POST to your notification_url. Metadata needed for verification is in the request headers; the event details are in the JSON body. Use the table below: each Key is the name to read; Value is where that value comes from.
| Key | Value (where it comes from) |
|---|---|
X-Rsom-Event | HTTP header on the incoming webhook request (same name). |
X-Rsom-Event-Id | HTTP header — unique id for this delivery; use for deduplication. |
X-Rsom-Timestamp | HTTP header — Unix timestamp string used inside the HMAC message. |
X-Rsom-Signature | HTTP header — hex-encoded HMAC your server must match (see below). |
| Raw request body | The exact POST body bytes as received (read before parsing JSON). |
| Webhook signing secret | Not sent in the request. You obtain it from RsomPay when your integration partner is onboarded; it is the only HMAC key. |
Verify: Use the webhook signing secret as the HMAC key. Hash the exact string X-Rsom-Timestamp + "." + raw_body with HMAC-SHA256, encode as lowercase hex, then compare to X-Rsom-Signature using a timing-safe comparison.
const crypto = require('crypto');
function verifyRsomWebhook(rawBody, headers, webhookSigningSecret) {
const timestamp = headers['x-rsom-timestamp'];
const signature = headers['x-rsom-signature'];
const expected = crypto.createHmac('sha256', webhookSigningSecret)
.update(`${timestamp}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
import hmac, hashlib
def verify_rsom_webhook(raw_body: bytes, timestamp: str, signature: str,
webhook_signing_secret: str) -> bool:
key = webhook_signing_secret.encode()
message = f"{timestamp}.".encode() + raw_body
expected = hmac.new(key, message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
function verifyRsomWebhook(
string $rawBody, string $timestamp, string $signature,
string $webhookSigningSecret
): bool {
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $webhookSigningSecret);
return hash_equals($expected, $signature);
}
event_id across retries — deduplicate on your side.payment_url.payment.rsompay.com.GET /invoices/by-reference (primary).payment.completed / payment.failed when delivered.return_url with query parameters (UX only).return_url (HTTP 302). There is no intermediate RsomPay result page when return_url is set on the invoice.
Use GET /invoices/by-reference as your main check before you fulfill an order:
GET /invoices/by-reference?external_id=INV-PARTNER-001
Confirm data.status is paid and data.balance is 0.
| Channel | Role |
|---|---|
GET /invoices/by-reference | Primary — authoritative status from RsomPay |
Webhook payment.completed | Backup — if it arrives; still confirm via by-reference before final settlement |
return_url | UX only — not proof of payment (user may close the browser) |
| Parameter | Description |
|---|---|
status | success, failed, or pending |
external_id | Your invoice id |
reference_number | RsomPay invoice number (e.g. INV-42-001) |
payment_reference | Gateway reference (if available) |
amount_paid | Amount for this payment |
currency_code | e.g. SAR |
https://partner.example.com/payment/return?status=success&external_id=INV-PARTNER-001&reference_number=INV-42-001&payment_reference=GW-123&amount_paid=103.5¤cy_code=SAR
On data.status and webhook invoice.status for external API invoices:
| Status | Meaning |
|---|---|
issued | Issued — awaiting payment |
paid | Paid in full (balance is 0) |
canceled | Cancelled — do not collect |
failed | Last payment attempt failed; customer may try again via payment_url |
| HTTP | Typical cause |
|---|---|
401 | Missing or invalid token |
403 | Wrong token type or suspended client |
404 | Invoice not found |
422 | Validation error |
500 | Server error |
| Issue | Fix |
|---|---|
| URL rejected | notification_url / return_url must be HTTPS and publicly reachable |
| by-reference 422 | Provide external_id or reference_number |
customer_id on by-reference | Not supported — use GET /transactions instead |
Validation messages may be localized — rely on HTTP status and field keys.
client-external-api token from RsomPay admin Integration tab.X-Rsom-Signature (see §7.5).POST /invoices with a unique external_id — save payment_url.GET /invoices/by-reference?external_id=... — confirm status is issued.payment_url.GET /invoices/by-reference — status is paid, balance is 0.payment.completed webhook (X-Rsom-Signature) if delivered.return_url query parameters in the browser (not settlement proof).This guide documents v1 (/api/external/v1).
| Method | Path | Purpose |
|---|---|---|
POST | /invoices | Create invoice |
GET | /invoices/by-reference | Query status (external_id or reference_number) |
GET | /transactions | Customer transactions (customer_id required) |
| Production | Sandbox | |
|---|---|---|
| Integration API | integration.rsompay.com | integration-demo.rsompay.com |
| Payment page | payment.rsompay.com | payment-demo.rsompay.com |
Download Postman collection (JSON)
customer_id in Postman is only for GET /transactions, not invoice lookup.
Technical support: tech@bseeds.sa
Website: https://rsompay.com
| Date | Notes |
|---|---|
| 2026-05-18 | Initial partner guide; docs site split to static hosting (docs.rsompay.com) |