The definitive path from first register to live in the Super App, plus
the two runtime systems every partner touches: orders (fulfillment) and
signed webhooks (event delivery).
1. Full lifecycle#
Phase gates#
2. Stage-by-stage walkthrough#
Stage A — Onboarding (Days 0–1)#
{
"organizationStatus": "ONBOARDING",
"verificationStatus": "DRAFT",
"profileIncomplete": true,
"missingFields": ["bankName: is required", "iban: is required"],
"missingDocuments": ["REP_ID"],
"canSubmitVerification": false,
"canCreateApplication": false,
"canPublishListing": false
}
Status enums — organization & verification#
| Event | Org status | Verification status |
|---|
| Register | ONBOARDING | DRAFT |
| Submit KYC | PENDING_VERIFICATION | SUBMITTED |
| Admin review | PENDING_VERIFICATION | UNDER_REVIEW |
| Admin approve | APPROVED | APPROVED |
| Admin reject | REJECTED | REJECTED |
the two runtime systems every partner touches: orders (fulfillment) and
signed webhooks (event delivery).
1. Full lifecycle#
Phase gates#
2. Stage-by-stage walkthrough#
Stage A — Onboarding (Days 0–1)#
6
Respond to info requests (if any)
{
"organizationStatus": "ONBOARDING",
"verificationStatus": "DRAFT",
"profileIncomplete": true,
"missingFields": ["bankName: is required", "iban: is required"],
"missingDocuments": ["REP_ID"],
"canSubmitVerification": false,
"canCreateApplication": false,
"canPublishListing": false,
"pendingInfoRequestCount": 0
}
Status enums — organization & verification#
| Event | Org status | Verification status |
|---|
| Register | ONBOARDING | DRAFT |
| Submit KYC | PENDING_VERIFICATION | SUBMITTED |
| Admin review | PENDING_VERIFICATION | UNDER_REVIEW |
| Admin approve | APPROVED | APPROVED |
| Admin reject | REJECTED | REJECTED |
| Admin requests info | PENDING_VERIFICATION | INFO_REQUESTED |
| Admin suspend | SUSPENDED | SUSPENDED |
Once org = APPROVED you can create an application.
If the admin needs additional details, verification moves to INFO_REQUESTED.
Each requested item appears as a separate info request.When all info requests have been responded to, verification is automatically
resubmitted for review. No separate submit step is needed.
If the admin requests an edit to an existing field (e.g. "wrong tax number"),
use PATCH /developer-portal/company to update the field first, then
respond to the info request with a message like "Tax number corrected to
1234567890". The admin will see both the updated field and your response when
they review. List info requests#
GET /developer-portal/company/info-requests — Bearer{
"infoRequests": [
{
"id": "1",
"request": "Company website not working — please verify",
"response": null,
"status": "PENDING",
"requestedAt": "2026-09-01T08:00:00.000Z",
"respondedAt": null
}
]
}
Respond to an info request#
POST /developer-portal/company/info-requests/:id/respond — Bearer{
"response": "https://www.example.com — site is live"
}
{
"id": "1",
"status": "RESPONDED",
"respondedAt": "2026-09-01T10:00:00.000Z",
"verificationStatus": "SUBMITTED"
}
verificationStatus is non-null when all requests are answered and
verification was auto-resubmitted.Stage B — Build the service (Days 2–5)#
Offers payload#
{
"titleEn": "Silver top-up",
"titleAr": "تعبئة فضية",
"descriptionEn": "Instant digital top-up",
"design": "banner",
"linkType": "offer",
"url": "https://partner.acme.sa/buy/silver",
"price": "49.99",
"originalPrice": "79.99",
"currency": "SAR",
"inStock": true,
"perUserLimit": 5,
"badgeLabel": "Best seller",
"badgeColor": "#E8553C",
"fulfillmentMode": "INSTANT"
}
Offer price is a decimal string (≤ 7 integer + 2 fraction digits); titles are
capped at 200 chars; a listing holds at most 20 offer cards. For composing
panels, card styles, and layout recipes, see
Panels & storefront. Fulfillment modes#
INSTANT — order completes immediately (PAYMENT_AUTHORIZED → COMPLETED).
PARTNER_CONFIRMATION — you drive fulfillment via
POST .../orders/:orderId/{confirm|start|complete|cancel}
(PAYMENT_AUTHORIZED → PENDING_PARTNER_CONFIRM → CONFIRMED → IN_PROGRESS → COMPLETED).
Stage C — Go live (Day 6)#
1.
Ensure your webhook endpoint is configured and tested in sandbox.
2.
Listing is PUBLISHED → customers can purchase in the Super App.
3.
Monitor order webhooks and fulfill via confirmation endpoints if needed.
3. The purchase moment (customer side)#
Purchases are idempotent via the x-idempotency-key header — a replayed
key returns the original order instead of charging twice.
If the wallet PIN is missing, the API returns 401 PIN_REQUIRED so the app
shows the PIN sheet and retries with pin.
Settlement — the developer's net#
where S_{net} is the amount settled to your payout account, qty is paid
units, and fee_{processing} is DigetPay's processing fee. Settlement events
arrive as webhook settlement.completed.Order status enum#
| Status | Meaning |
|---|
CREATED | Draft order row created |
PENDING_PAYMENT | Charge initiated against the card |
PAYMENT_AUTHORIZED | Charge captured, awaiting fulfillment |
PENDING_PARTNER_CONFIRM | Waiting for partner confirm |
CONFIRMED | Partner acknowledged the order |
IN_PROGRESS | Partner is fulfilling (start) |
COMPLETED | Fulfilled (complete) / instant |
CANCELLED | Partner cancelled |
FAILED | Payment failed / expired |
REFUND_PENDING / REFUNDED | Refund underway / completed |
4. Orders endpoints#
4.1 List application orders#
Response 200 — { items, meta }{
"id": "1024",
"status": "COMPLETED",
"fulfillmentStatus": "COMPLETED",
"fulfillmentMode": "INSTANT",
"offer": { "offerId": "12", "titleEn": "Silver top-up", "price": "49.99", "currency": "SAR", "quantity": 1 },
"fulfillment": { "type": "deeplink", "url": "https://partner.acme.sa/redeem/7f3a…", "token": null, "allowedOrigins": [] },
"createdAt": "2026-08-18T10:04:12.000Z"
}
4.2 Advance an order (partner-confirmation models)#
| Action | Meaning | Allowed from |
|---|
confirm | Accept the order | PENDING_PARTNER_CONFIRM |
start | Begin fulfillment | CONFIRMED |
complete | Finish fulfillment | IN_PROGRESS |
cancel | Cancel (money refund flows separately) | PENDING_PARTNER_CONFIRM |
cancel does not itself move money back. Refunds are driven by the refund
workflow / gateway and surface via the payment.refunded webhook.
Order actions are validated transitions — an illegal transition returns 400.
Take the order status from the webhook, not from a local cache.
5. Webhook endpoints#
5.1 Create an endpoint#
{
"environment": "SANDBOX",
"url": "https://api.acme.sa/webhooks/digetpay",
"events": ["payment.completed", "order.completed"],
"retryLimit": 3
}
environment ∈ SANDBOX | PRODUCTION
events ≤ 20, chosen from the catalog below
retryLimit ∈ 1–5 (default 3)
5.2 Event catalog#
| Event | Raised when |
|---|
application.approved | Company verification approved |
application.rejected | Verification rejected |
payment.created | Card charge initiated |
payment.completed | Charge captured |
payment.failed | Charge declined / failed |
payment.refunded | Refund finalized |
order.confirmation_required | Waiting for partner confirm |
order.confirmed | Partner confirmed |
order.in_progress | Partner started fulfillment |
order.completed | Fulfillment complete (or INSTANT) |
order.cancelled | Order cancelled |
order.expired | Order auto-expired |
order.failed | Order failed |
service.completed | End-service milestone completed |
settlement.completed | A settlement batch was paid out |
webhook.connection_test | Endpoint /test button (not subscribable) |
6. Delivery envelope & signatures#
Every delivery is a POST (10 s timeout) with JSON body:{
"event": "payment.completed",
"environment": "SANDBOX",
"applicationId": "42",
"createdAt": "2026-08-18T10:04:12.000Z",
"data": { "orderId": "1024", "amount": "49.99", "currency": "SAR" }
}
The outer fields (event, environment, applicationId, createdAt) are
always present; the business object lives in data. Prefer the
X-DigetPay-Event header for routing and the event body field for
verification.Signature construction#
Headers every delivery carries:| Header | Example value |
|---|
Content-Type | application/json |
User-Agent | DigetPay-Webhooks/1.0 |
X-DigetPay-Timestamp | 1784544252 (Unix seconds) |
X-DigetPay-Signature | sha256=… (hex HMAC over {ts}.{rawBody}) |
X-DigetPay-Event | payment.completed |
Verify the signature and check X-DigetPay-Timestamp is fresh (e.g. within
±5 minutes) before trusting any payload.
Retry & backoff policy#
| Attempt | Wait before next | Trigger |
|---|
| Any | — | success = HTTP 2xx |
| 1st | ~250 ms | network error, 429, 5xx |
| 2nd | ~500 ms | same |
| 3rd | ~1 s | same |
| 4th | ~2 s | same |
| last | stop | failure logged to deliveries |
retryLimit caps attempts at 1–5 (default 3).
Non-retryable 4xx responses (e.g. 400) are not retried.
Every attempt is recorded in the deliveries log with httpStatus, attempts,
lastError, and success.
Endpoint test#
POST .../webhooks/:endpointId/test sends a one-shot webhook.connection_test
delivery with data.message = "DigetPay webhook connection test" (no retries)
and returns { httpStatus, success, errorMessage }.Sample payloads#
7. Deliveries log#
| Field | Type | Meaning |
|---|
endpointId | string | Webhook endpoint that received it |
eventType | string | Delivered event |
httpStatus | number? | Last HTTP status (null = network error) |
success | boolean | Whether the last attempt was 2xx |
attempts | integer | How many attempts were made |
lastError | string? | Last failure reason |
Use the deliveries log to debug silent misses — check that events[]
subscription, active, and retryLimit all look right first.
8. Testing flow (staging walkthrough)#
9. Go-live checklist#
Orders + signed webhooks cover the runtime contract. Integration is complete
once your endpoint verifies signatures and your fulfillment actions move orders
to COMPLETED.
| Admin suspend | SUSPENDED | SUSPENDED |Once org = APPROVED you can create an application.
Stage B — Build the service (Days 2–5)#
Offers payload#
{
"titleEn": "Silver top-up",
"titleAr": "تعبئة فضية",
"descriptionEn": "Instant digital top-up",
"design": "banner",
"linkType": "offer",
"url": "https://partner.acme.sa/buy/silver",
"price": "49.99",
"originalPrice": "79.99",
"currency": "SAR",
"inStock": true,
"perUserLimit": 5,
"badgeLabel": "Best seller",
"badgeColor": "#E8553C",
"fulfillmentMode": "INSTANT"
}
Offer price is a decimal string (≤ 7 integer + 2 fraction digits); titles are
capped at 200 chars; a listing holds at most 20 offer cards. For composing
panels, card styles, and layout recipes, see
Panels & storefront. Fulfillment modes#
INSTANT — order completes immediately (PAYMENT_AUTHORIZED → COMPLETED).
PARTNER_CONFIRMATION — you drive fulfillment via
POST .../orders/:orderId/{confirm|start|complete|cancel}
(PAYMENT_AUTHORIZED → PENDING_PARTNER_CONFIRM → CONFIRMED → IN_PROGRESS → COMPLETED).
Stage C — Go live (Day 6)#
1.
Ensure your webhook endpoint is configured and tested in sandbox.
2.
Listing is PUBLISHED → customers can purchase in the Super App.
3.
Monitor order webhooks and fulfill via confirmation endpoints if needed.
3. The purchase moment (customer side)#
Purchases are idempotent via the x-idempotency-key header — a replayed
key returns the original order instead of charging twice.
If the wallet PIN is missing, the API returns 401 PIN_REQUIRED so the app
shows the PIN sheet and retries with pin.
Settlement — the developer's net#
where S_{net} is the amount settled to your payout account, qty is paid
units, and fee_{processing} is DigetPay's processing fee. Settlement events
arrive as webhook settlement.completed.Order status enum#
| Status | Meaning |
|---|
CREATED | Draft order row created |
PENDING_PAYMENT | Charge initiated against the card |
PAYMENT_AUTHORIZED | Charge captured, awaiting fulfillment |
PENDING_PARTNER_CONFIRM | Waiting for partner confirm |
CONFIRMED | Partner acknowledged the order |
IN_PROGRESS | Partner is fulfilling (start) |
COMPLETED | Fulfilled (complete) / instant |
CANCELLED | Partner cancelled |
FAILED | Payment failed / expired |
REFUND_PENDING / REFUNDED | Refund underway / completed |
4. Orders endpoints#
4.1 List application orders#
Response 200 — { items, meta }{
"id": "1024",
"status": "COMPLETED",
"fulfillmentStatus": "COMPLETED",
"fulfillmentMode": "INSTANT",
"offer": { "offerId": "12", "titleEn": "Silver top-up", "price": "49.99", "currency": "SAR", "quantity": 1 },
"fulfillment": { "type": "deeplink", "url": "https://partner.acme.sa/redeem/7f3a…", "token": null, "allowedOrigins": [] },
"createdAt": "2026-08-18T10:04:12.000Z"
}
4.2 Advance an order (partner-confirmation models)#
| Action | Meaning | Allowed from |
|---|
confirm | Accept the order | PENDING_PARTNER_CONFIRM |
start | Begin fulfillment | CONFIRMED |
complete | Finish fulfillment | IN_PROGRESS |
cancel | Cancel (money refund flows separately) | PENDING_PARTNER_CONFIRM |
cancel does not itself move money back. Refunds are driven by the refund
workflow / gateway and surface via the payment.refunded webhook.
Order actions are validated transitions — an illegal transition returns 400.
Take the order status from the webhook, not from a local cache.
5. Webhook endpoints#
5.1 Create an endpoint#
{
"environment": "SANDBOX",
"url": "https://api.acme.sa/webhooks/digetpay",
"events": ["payment.completed", "order.completed"],
"retryLimit": 3
}
environment ∈ SANDBOX | PRODUCTION
events ≤ 20, chosen from the catalog below
retryLimit ∈ 1–5 (default 3)
5.2 Event catalog#
| Event | Raised when |
|---|
application.approved | Company verification approved |
application.rejected | Verification rejected |
payment.created | Card charge initiated |
payment.completed | Charge captured |
payment.failed | Charge declined / failed |
payment.refunded | Refund finalized |
order.confirmation_required | Waiting for partner confirm |
order.confirmed | Partner confirmed |
order.in_progress | Partner started fulfillment |
order.completed | Fulfillment complete (or INSTANT) |
order.cancelled | Order cancelled |
order.expired | Order auto-expired |
order.failed | Order failed |
service.completed | End-service milestone completed |
settlement.completed | A settlement batch was paid out |
webhook.connection_test | Endpoint /test button (not subscribable) |
6. Delivery envelope & signatures#
Every delivery is a POST (10 s timeout) with JSON body:{
"event": "payment.completed",
"environment": "SANDBOX",
"applicationId": "42",
"createdAt": "2026-08-18T10:04:12.000Z",
"data": { "orderId": "1024", "amount": "49.99", "currency": "SAR" }
}
The outer fields (event, environment, applicationId, createdAt) are
always present; the business object lives in data. Prefer the
X-DigetPay-Event header for routing and the event body field for
verification.Signature construction#
Headers every delivery carries:| Header | Example value |
|---|
Content-Type | application/json |
User-Agent | DigetPay-Webhooks/1.0 |
X-DigetPay-Timestamp | 1784544252 (Unix seconds) |
X-DigetPay-Signature | sha256=… (hex HMAC over {ts}.{rawBody}) |
X-DigetPay-Event | payment.completed |
Verify the signature and check X-DigetPay-Timestamp is fresh (e.g. within
±5 minutes) before trusting any payload.
Retry & backoff policy#
| Attempt | Wait before next | Trigger |
|---|
| Any | — | success = HTTP 2xx |
| 1st | ~250 ms | network error, 429, 5xx |
| 2nd | ~500 ms | same |
| 3rd | ~1 s | same |
| 4th | ~2 s | same |
| last | stop | failure logged to deliveries |
retryLimit caps attempts at 1–5 (default 3).
Non-retryable 4xx responses (e.g. 400) are not retried.
Every attempt is recorded in the deliveries log with httpStatus, attempts,
lastError, and success.
Endpoint test#
POST .../webhooks/:endpointId/test sends a one-shot webhook.connection_test
delivery with data.message = "DigetPay webhook connection test" (no retries)
and returns { httpStatus, success, errorMessage }.Sample payloads#
7. Deliveries log#
| Field | Type | Meaning |
|---|
endpointId | string | Webhook endpoint that received it |
eventType | string | Delivered event |
httpStatus | number? | Last HTTP status (null = network error) |
success | boolean | Whether the last attempt was 2xx |
attempts | integer | How many attempts were made |
lastError | string? | Last failure reason |
Use the deliveries log to debug silent misses — check that events[]
subscription, active, and retryLimit all look right first.
8. Testing flow (staging walkthrough)#
9. Go-live checklist#
Orders + signed webhooks cover the runtime contract. Integration is complete
once your endpoint verifies signatures and your fulfillment actions move orders
to COMPLETED.