Skip to content
Akedly
V2.0 Widgets

Results and callbacks

Read the webhook payload your server receives after a verification, and where to verify its signature.

Step 4: Handle Callbacks

Backend Webhook Payload

If you configure a backendCallbackURL in your pipeline settings, Akedly sends a POST request to your server with complete verification details.

Webhook Timing:

The webhook is sent:

  • Immediately after successful verification (OTP verified)
  • Immediately after failed verification (invalid OTP, expired, etc.)

V2 Widget callbacks are signed

The V2 Widget callback is an envelope with top-level status and timestamp, plus attempt, transaction, metadata, and optional error data.

  • Name
    Payload
    Type
    object
    Description
    The envelope contains status, timestamp, widgetAttempt, transaction, transactionReq, publicMetadata, and privateMetadata; failed callbacks may also include error.
  • Name
    Dev-mode marker
    Type
    widgetAttempt.isTest
    Description
    A test-pair attempt is marked by widgetAttempt.isTest. It is not a top-level webhook field.
  • Name
    Signature
    Type
    svix headers
    Description
    The widget success path supplies the pipeline signing secret to the common dispatcher, which generates svix-id, svix-timestamp, and svix-signature.

Webhook Payloads

{
  "status": "success",
  "timestamp": "2026-10-01T12:00:42.150Z",
  "widgetAttempt": {
    "attemptId": "attempt_a1b2c3d4e5f67890",
    "widgetId": "widget_x1y2z3",
    "userId": "6821bb199f9cfd6b2754e6ef",
    "status": "verified",
    "isTest": false,
    "verificationAddress": {
      "phoneNumber": "+201****7890",
      "email": null
    },
    "otpConfig": {
      "digits": 6,
      "resendCount": 0
    },
    "publicMetadata": {
      "orderId": "order_abc789"
    },
    "createdAt": "2026-10-01T11:59:40.000Z",
    "expiresAt": "2026-10-01T12:04:40.000Z",
    "captchaVerifiedAt": "2026-10-01T11:59:52.500Z",
    "otpRequestedAt": "2026-10-01T11:59:59.800Z",
    "completedAt": "2026-10-01T12:00:42.120Z"
  },
  "transaction": {
    "transactionID": "ae2eacaebe3ed78b105498d5d0cfe54f11b4130a0847d67b36927752148679e5",
    "status": "Successful",
    "verificationAddress": {
      "phoneNumber": "+201234567890"
    },
    "OTP": "123456",
    "resolvedChannel": "whatsapp",
    "channels": [
      "whatsapp"
    ],
    "isTest": false,
    "userID": "6821bb199f9cfd6b2754e6ef",
    "pipelineID": "6821bba09f9cfd6b2754e752",
    "creationDate": "2026-10-01T12:00:00.000Z",
    "expirationDate": "2026-10-01T12:03:00.000Z",
    "updateDate": "2026-10-01T12:00:42.120Z"
  },
  "transactionReq": {
    "_id": "6a1f0c2f9d1b4a0012345679",
    "mainTransactionID": "ae2eacaebe3ed78b105498d5d0cfe54f11b4130a0847d67b36927752148679e5",
    "status": "Successful",
    "isTest": false,
    "channels": [
      "whatsapp"
    ],
    "sentVerificationDate": "2026-10-01T12:00:01.200Z",
    "inputOTP": "123456",
    "verificationDate": "2026-10-01T12:00:42.120Z",
    "creationDate": "2026-10-01T12:00:00.300Z",
    "expirationDate": "2026-10-01T12:03:00.300Z"
  },
  "publicMetadata": {
    "orderId": "order_abc789"
  },
  "privateMetadata": {
    "internalUserId": "internal_xyz"
  }
}

Verifying Webhook Signatures

When signature headers are present, the JSON payload above is what gets signed — verify the signature before parsing the body, on the raw request bytes. A webhook with no svix-signature header is neither trusted nor rejected: the examples above answer 200 and confirm the attempt with a status lookup before marking anything verified.