Build payments into your product with TPE virtual. Start integrating →
Logo

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

MethodPathDescription
GET/webhooksList your webhooks (paginated).
POST/webhooksCreate a webhook. Returns the secret once.
GET/webhooks/eventsList 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-secretRotate the signing secret.
GET/webhooks/{id}/deliveriesList delivery attempts.
POST/webhooks/{id}/deliveries/{deliveryId}/redeliverRetry a delivery.

The webhook object

FieldTypeDescription
idstring (uuid)Unique identifier.
urlstringYour HTTPS endpoint.
descriptionstring | nullOptional label.
eventsarraySubscribed event types. Empty array = all events.
is_activebooleanWhether deliveries are sent.
created_at / updated_atstring (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

Request
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"
200 OK
{
  "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

Status200 OK
BodyA paginated array of webhook objects.

Errors follow the shared envelope. See Responses.

Create a webhook

FieldTypeRequiredNotes
urlstringyesMust be https://, max 2048.
descriptionstringnoMax 255.
eventsarraynoSubset of valid event values. Omit/empty = all.
is_activebooleannoDefaults to active.
Request
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"]
  }'
201 Created
{
  "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

Status201 Created
BodyThe 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

Request
curl https://api.mytpe.app/api/ext/webhooks/events \
  -H "X-Api-Key: $MYTPE_API_KEY" \
  -H "X-Api-Secret: $MYTPE_API_SECRET"
200 OK
{
  "data": [
    { "value": "transaction.paid", "label": "Transaction paid" },
    { "value": "transaction.failed", "label": "Transaction failed" },
    { "value": "payment_link.exhausted", "label": "Payment link exhausted" }
  ]
}

Response

Status200 OK
BodyAn 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

Request
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"
200 OK
{
  "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

Status200 OK
BodyOne 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).

Request
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 }'
200 OK
{
  "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

Status200 OK
BodyThe updated webhook object, wrapped in data.

Errors follow the shared envelope. See Responses.

Delete a webhook

Request
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"
204 No Content
(empty body)

Response

Status204 No Content
BodyEmpty. No body at all — do not try to parse one.

Errors follow the shared envelope. See Responses.

Regenerate the secret

Request
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"
200 OK
{ "secret": "whsec_a1b2c3d4e5f6071829304a5b6c7d8e9f" }

The previous secret stops validating immediately. Update your endpoint before rotating in production.

Response

Status200 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.

Request
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"
200 OK
{
  "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

Status200 OK
BodyA paginated array of delivery attempts, newest first.

Errors follow the shared envelope. See Responses.

Redeliver

Queue a fresh attempt for a past delivery.

Request
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"
200 OK
{ "queued": true }

Response

Status200 OK
BodyA 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...c12d

v1 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).

PHP
[$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.

On this page