/api/v1/merchants/registerCreate a merchant and receive credentials by email.
- Auth
- No API key required
Developer documentation
Everything here is based on the current public DinarGuard API routes, request schemas, response serializers, and webhook signing behavior.
Quickstart
Create a merchant and receive merchant_id, api_key, and webhook_secret by email.
Open the DinarGuard bot, send /start, then send the Telegram connect command from your credentials email.
Your backend creates a session with amount, metadata, and webhook URL.
Your backend uploads the receipt image. DinarGuard extracts fields, validates them, and sends Telegram review.
Base URL
Merchant API routes use Authorization: Bearer YOUR_API_KEY. Keep the API key server-side. DinarGuard only sends credentials when they are generated and never returns them in API responses.
Base URL: https://dinarguard.mohamedazizheni.tn
Authorization: Bearer dg_live_your_api_keyEndpoint map
/api/v1/merchants/registerCreate a merchant and receive credentials by email.
/api/v1/payment-sessionsCreate a D17 payment session.
/api/v1/payment-sessions/{paymentId}/screenshotUpload a customer receipt screenshot.
/api/v1/payment-sessions/{paymentId}Fetch one payment session.
/api/v1/paymentsList merchant payments.
/api/v1/payment-sessions/{paymentId}/cancelCancel a payment that is not finalized.
/api/v1/payment-sessions/{paymentId}/webhook/retryManually retry webhook delivery for a payment.
Merchant registration
This endpoint creates a merchant, generates an API key and webhook secret, emails the credentials, then returns public merchant details.
| Field | Type | Notes |
|---|---|---|
business_name | string | Required. 2 to 120 chars. Must be unique case-insensitively. |
contact_email | Required. Max 200 chars. Must be unique case-insensitively. | |
d17_receiver_phone | string | Required. Must normalize to a valid Tunisian phone number. |
d17_receiver_name | string | Optional. Max 120 chars. Defaults to business_name. |
curl -X POST https://dinarguard.mohamedazizheni.tn/api/v1/merchants/register \
-H "Content-Type: application/json" \
-d '{
"business_name": "Demo Store",
"contact_email": "owner@example.com",
"d17_receiver_phone": "+21699980126",
"d17_receiver_name": "Demo Store"
}'Create session
| Field | Type | Notes |
|---|---|---|
amount | number | Required. Positive amount. Stored with 3 decimals. |
currency | "TND" | Optional. Defaults to TND. No other currency is accepted. |
external_customer_id | string | Optional. 1 to 200 chars. Your customer or order reference. |
description | string | Optional. Up to 500 chars. |
webhook_url | url | Required. Must be HTTPS, no credentials, public IP. Blocked private/local IPs. Max 10s timeout. |
metadata | object | Optional. Defaults to {}. Returned in payment objects and webhooks. |
expires_in_minutes | integer | Optional. 5 to 1440. Defaults to 120. |
curl -X POST https://dinarguard.mohamedazizheni.tn/api/v1/payment-sessions \
-H "Authorization: Bearer dg_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 50,
"currency": "TND",
"external_customer_id": "client_123",
"description": "Monthly plan",
"webhook_url": "https://your-app.com/api/dinarguard",
"metadata": { "order_id": "order_789" },
"expires_in_minutes": 120
}'Payment object
| Field | Type | Notes |
|---|---|---|
payment_id | string | Payment ID, generated with pay_ prefix. |
status | string | External status: pending, processing, provisional, needs_review, approved, rejected, expired. |
internal_status | string | Detailed status returned by the current API response. |
amount | number | Expected amount. |
currency | string | Currency. Currently TND. |
external_customer_id | string | null | Your customer reference. |
description | string | null | Optional payment description. |
webhook_url | string | Callback URL used for this payment. |
metadata | object | Your metadata object. |
extracted | object | Receipt extraction result. |
validation_errors | array | Validation error codes saved for the payment. |
expires_at | string | ISO timestamp. |
created_at | string | ISO timestamp. |
updated_at | string | ISO timestamp. |
{
"payment_id": "pay_abc123",
"status": "pending",
"internal_status": "created",
"amount": 50,
"currency": "TND",
"external_customer_id": "client_123",
"description": "Monthly plan",
"webhook_url": "https://your-app.com/api/dinarguard",
"metadata": { "order_id": "order_789" },
"extracted": {
"sender_name": null,
"sender_phone": null,
"transaction_number": null,
"amount": null,
"currency": null,
"payment_datetime": null,
"confidence_score": null
},
"validation_errors": [],
"expires_at": "2026-05-11T12:00:00.000Z",
"created_at": "2026-05-11T10:00:00.000Z",
"updated_at": "2026-05-11T10:00:00.000Z"
}Receipt upload
Uploads must be multipart form data. The image must be JPEG, PNG, or WebP and 5 MB or smaller. Duplicate screenshots for the same merchant return 409.
| Field | Type | Notes |
|---|---|---|
file | File | Required multipart field. Must be JPEG, PNG, or WebP. Max 5MB. Production ignores merchant OCR overrides. |
curl -X POST https://dinarguard.mohamedazizheni.tn/api/v1/payment-sessions/pay_abc123/screenshot \
-H "Authorization: Bearer dg_live_your_api_key" \
-F "file=@receipt.png"Statuses
| Detailed status | API status | Meaning |
|---|---|---|
created | pending | Payment session was created. No receipt uploaded yet. |
screenshot_uploaded | pending | Receipt was accepted by the API. |
processing | processing | Receipt is being extracted and checked. |
extraction_failed | needs_review | Receipt extraction failed. |
validation_failed | needs_review | Validation did not pass. |
needs_review | needs_review | Telegram review is expected. |
provisionally_approved | provisional | Validation passed and provisional access is enabled. |
approved | approved | Telegram approved the payment. |
rejected | rejected | Telegram rejected the payment. |
expired | expired | Payment expired or was cancelled. |
Validation
DinarGuard compares extracted receipt data against the payment row and merchant receiver phone. These are the validation codes saved in validation_errors.
| Code | Meaning |
|---|---|
amount_mismatch | Extracted amount is missing or differs from the session amount. |
currency_mismatch | Extracted currency exists and is not TND. |
missing_transaction_number | No authorization or transaction number was extracted. |
duplicate_transaction_number | Another payment for the same merchant already used this transaction number. |
invalid_sender_phone | Sender phone was present but not a valid Tunisian phone. |
receiver_phone_mismatch | Extracted receiver phone differs from the merchant D17 receiver phone. |
low_confidence | Extraction confidence is below 0.65. |
extraction_failed | Receipt extraction threw an error. |
Webhooks
Webhooks are delivered when payments are approved, rejected, expired, cancelled through the cancel endpoint, or retried manually. The event name is payment. plus the external status.
Headers:
content-type: application/json
x-dinarguard-timestamp: 1778493600
x-dinarguard-signature: hmac_sha256_hex
Signature input:
timestamp + "." + raw_request_body
Payload:
{
"event_id": "evt_abc123",
"event": "payment.approved",
"payment_id": "pay_abc123",
"status": "approved",
"amount": 50,
"currency": "TND",
"external_customer_id": "client_123",
"metadata": { "order_id": "order_789" }
}Errors
Error responses use JSON with an error string. Some validation failures also include a details object, and duplicate screenshots include duplicate_payment_id.
| Status | When it happens |
|---|---|
400 | Invalid JSON body, invalid schema fields, invalid phone, invalid multipart form, missing file, invalid image type, or image larger than 5 MB. |
401 | Missing or invalid authorization. |
404 | Payment not found, or payment no longer cancelable. |
409 | Duplicate merchant registration field, duplicate screenshot, or finalized payment upload. |
429 | Too many requests. Rate limit exceeded. |
500 | Receipt upload failed or another server error occurred. |
502 | Merchant registration credentials email could not be sent. |
Listing and actions
GET https://dinarguard.mohamedazizheni.tn/api/v1/payment-sessions/pay_abc123
Authorization: Bearer dg_live_your_api_keyGET https://dinarguard.mohamedazizheni.tn/api/v1/payments?status=approved&limit=50
Authorization: Bearer dg_live_your_api_key
limit is capped at 100.POST https://dinarguard.mohamedazizheni.tn/api/v1/payment-sessions/pay_abc123/cancel
Authorization: Bearer dg_live_your_api_key
Sets the payment status to expired.POST https://dinarguard.mohamedazizheni.tn/api/v1/payment-sessions/pay_abc123/webhook/retry
Authorization: Bearer dg_live_your_api_keyData model
These are public API data groups developers interact with. Internal storage details are intentionally omitted from the public docs.
merchant_idapi_keywebhook_secretpayment_idstatusamountcurrencyexternal_customer_idmetadataexpires_atsender_phonetransaction_numberamountcurrencyconfidence_scorevalidation_errorsevent_ideventpayment_idstatusamountcurrencymetadata