Webhooks
Subscribe HTTPS endpoints to real-time events, verify signatures, and inspect deliveries.
Webhooks push events to your server as they happen — a payment succeeds, a link is exhausted, a KYC resource is approved. Register one or more HTTPS endpoints, each subscribing to the events it cares about.
Statuses reference
The values inside webhook payloads (transaction status, link status, KYC status, and the rest) are the same enums documented in Statuses and enums. The list of event types is in Statuses and enums → Webhook events.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /webhooks | List your webhooks (paginated). |
POST | /webhooks | Create a webhook. Returns the secret once. |
GET | /webhooks/events | List available event types. |
GET | /webhooks/{id} | Retrieve a webhook. |
PATCH | /webhooks/{id} | Update a webhook. |
DELETE | /webhooks/{id} | Delete a webhook (204). |
POST | /webhooks/{id}/regenerate-secret | Rotate the signing secret. |
GET | /webhooks/{id}/deliveries | List delivery attempts. |
POST | /webhooks/{id}/deliveries/{deliveryId}/redeliver | Retry a delivery. |
The webhook object
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique identifier. |
url | string | Your HTTPS endpoint. |
description | string | null | Optional label. |
events | array | Subscribed event types. Empty array = all events. |
is_active | boolean | Whether deliveries are sent. |
created_at / updated_at | string (ISO 8601) | Timestamps. |
secret is not part of the webhook object. Create and regenerate return it
as a top-level key beside data, never inside it — reading data.secret
yields undefined. It is shown once and never returned again, so store it the
moment you receive it.
List webhooks
curl "https://api.mytpe.app/api/ext/webhooks?per_page=15" \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{
"data": [
{
"id": "6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e",
"url": "https://example.com/webhooks/mytpe",
"description": "Production",
"events": ["transaction.paid", "payment_link.exhausted"],
"is_active": true,
"created_at": "2026-06-09T12:00:00.000Z",
"updated_at": "2026-06-09T12:00:00.000Z"
}
],
"links": { "first": "...?page=1", "last": "...?page=1", "prev": null, "next": null },
"meta": { "current_page": 1, "from": 1, "to": 1, "per_page": 15, "last_page": 1, "total": 1 }
}Response
| Status | 200 OK |
| Body | A paginated array of webhook objects. |
Errors follow the shared envelope. See Responses.
Create a webhook
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Must be https://, max 2048. |
description | string | no | Max 255. |
events | array | no | Subset of valid event values. Omit/empty = all. |
is_active | boolean | no | Defaults to active. |
curl -X POST https://api.mytpe.app/api/ext/webhooks \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/mytpe",
"events": ["transaction.paid", "payment_link.exhausted"]
}'{
"data": {
"id": "6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e",
"url": "https://example.com/webhooks/mytpe",
"description": null,
"events": ["transaction.paid", "payment_link.exhausted"],
"is_active": true,
"created_at": "2026-06-09T12:00:00.000Z",
"updated_at": "2026-06-09T12:00:00.000Z"
},
"secret": "whsec_3f9a2b1c8d7e6f5a4b3c2d1e0f9a8b7c"
}Store the secret now — it is not returned again. Use regenerate-secret to rotate it.
Response
| Status | 201 Created |
| Body | The created webhook object in data, plus the signing secret as a top-level key beside it. The secret is shown once. |
Errors follow the shared envelope. See Responses.
List event types
curl https://api.mytpe.app/api/ext/webhooks/events \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{
"data": [
{ "value": "transaction.paid", "label": "Transaction paid" },
{ "value": "transaction.failed", "label": "Transaction failed" },
{ "value": "payment_link.exhausted", "label": "Payment link exhausted" }
]
}Response
| Status | 200 OK |
| Body | An array of { value, label } pairs in data. Subscribe using value; label is for display only. |
Errors follow the shared envelope. See Responses.
Retrieve a webhook
curl https://api.mytpe.app/api/ext/webhooks/6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{
"data": {
"id": "6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e",
"url": "https://example.com/webhooks/mytpe",
"description": "Production",
"events": ["transaction.paid", "payment_link.exhausted"],
"is_active": true,
"created_at": "2026-06-09T12:00:00.000Z",
"updated_at": "2026-06-09T12:00:00.000Z"
}
}Response
| Status | 200 OK |
| Body | One webhook object, wrapped in data. Never includes the secret. |
Errors follow the shared envelope. See Responses.
Update a webhook
Accepts the same fields as create (url, description, events, is_active).
curl -X PATCH https://api.mytpe.app/api/ext/webhooks/6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "events": ["transaction.paid", "transaction.failed"], "is_active": false }'{
"data": {
"id": "6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e",
"url": "https://example.com/webhooks/mytpe",
"description": "Production",
"events": ["transaction.paid", "transaction.failed"],
"is_active": false,
"created_at": "2026-06-09T12:00:00.000Z",
"updated_at": "2026-06-09T13:00:00.000Z"
}
}Response
| Status | 200 OK |
| Body | The updated webhook object, wrapped in data. |
Errors follow the shared envelope. See Responses.
Delete a webhook
curl -X DELETE https://api.mytpe.app/api/ext/webhooks/6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"(empty body)Response
| Status | 204 No Content |
| Body | Empty. No body at all — do not try to parse one. |
Errors follow the shared envelope. See Responses.
Regenerate the secret
curl -X POST https://api.mytpe.app/api/ext/webhooks/6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e/regenerate-secret \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{ "secret": "whsec_a1b2c3d4e5f6071829304a5b6c7d8e9f" }The previous secret stops validating immediately. Update your endpoint before rotating in production.
Response
| Status | 200 OK |
| Body | { "secret": "whsec_…" } only. No data key. The previous secret stops verifying immediately, so deploy the new one before rotating. |
Errors follow the shared envelope. See Responses.
List deliveries
Each delivery records one attempt to call your endpoint.
Query parameters: event_type, per_page.
curl "https://api.mytpe.app/api/ext/webhooks/6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e/deliveries?per_page=15" \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{
"data": [
{
"id": "ab12cd34-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"webhook_id": "6b7c8d9e-0f1a-2b3c-4d5e-6f7a8b9c0d1e",
"event_type": "transaction.paid",
"payload": { "event": "transaction.paid", "data": { "id": "8e7d...", "status": "paid" } },
"attempt": 1,
"http_status": 200,
"response_body": "OK",
"delivered_at": "2026-06-09T12:34:57.000Z",
"failed_at": null,
"created_at": "2026-06-09T12:34:56.000Z"
}
],
"links": { "first": "...?page=1", "last": "...?page=1", "prev": null, "next": null },
"meta": { "current_page": 1, "from": 1, "to": 1, "per_page": 15, "last_page": 1, "total": 1 }
}Response
| Status | 200 OK |
| Body | A paginated array of delivery attempts, newest first. |
Errors follow the shared envelope. See Responses.
Redeliver
Queue a fresh attempt for a past delivery.
curl -X POST https://api.mytpe.app/api/ext/webhooks/6b7c8d9e.../deliveries/ab12cd34.../redeliver \
-H "X-Api-Key: $MYTPE_API_KEY" \
-H "X-Api-Secret: $MYTPE_API_SECRET"{ "queued": true }Response
| Status | 200 OK |
| Body | A confirmation in meta.message. The retry is queued, not performed inline. |
Errors follow the shared envelope. See Responses.
Events
The taxonomy covers transactions, payment instances, payment links, forms, and KYC. See the full list with descriptions in Statuses and enums → Webhook events.
Delivery payload
Every delivery is a JSON POST to your url:
{
"event": "transaction.paid",
"created_at": "2026-06-09T12:34:56.000000Z",
"data": {
"id": "8e7d6c5b-4a39-2817-0f6e-5d4c3b2a1908",
"status": "paid",
"amount": "4500.00",
"currency": "DZD",
"payable_type": "payment_link",
"payable_id": "5a6b7c8d-9e0f-1a2b-3c4d-5e6f7a8b9c0d",
"metadata": { "order_id": "10428" }
}
}Payloads never include PANs or raw gateway data.
Verify signatures
Each delivery includes an X-Mytpe-Signature header:
X-Mytpe-Signature: t=1733745296,v1=5257a869e7ec...c12dv1 is HMAC_SHA256("{t}.{rawBody}", secret). Recompute it with your webhook's secret and compare
in constant time. Reject if it does not match, or if t is too old (guards against replay).
[$t, $v1] = sscanf($_SERVER['HTTP_X_MYTPE_SIGNATURE'], 't=%d,v1=%s');
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', "{$t}." . $body, $webhookSecret);
if (!hash_equals($expected, $v1) || abs(time() - $t) > 300) {
http_response_code(400);
exit;
}Respond 2xx quickly. Non‑2xx responses are retried; inspect attempts at GET /webhooks/{id}/deliveries and replay with the redeliver endpoint.