Skip to content
Akedly
V1.2 REST API

Verify and callbacks

Verify the OTP your user entered, then read the verification-result callback your server receives.

Step 3: Verify OTP

Submit the user's OTP input for verification using the transactionReqID from Step 2.

Request Body

  • Name
    transactionReqID
    Type
    string
    Description

    The transaction request ID returned in Step 2's data.transactionReqID.

  • Name
    otp
    Type
    string
    Description

    The OTP code entered by the user (must be sent as a string). Arabic-Indic (٠-٩) and Persian (۰-۹) digits are accepted and converted to ASCII digits.

  • Name
    returnTarget
    Type
    object
    Requirement
    Optional
    Description

    Pass this when you plan to offer passkey enrollment and need a proven SDK result. Use {"origin":"https://your-site.example"} for web or {"url":"myapp://akedly-passkey"} for a native deep link. Akedly signs the sanitized target into data.enrollmentToken.

Success Response

  • Name
    status
    Type
    string
    Description

    "success" on valid OTP.

  • Name
    data.verified
    Type
    boolean
    Description

    true when the OTP is verified successfully.

  • Name
    data.transactionID
    Type
    string
    Description

    The main transaction ID.

  • Name
    data.frontendCallbackURL
    Type
    string
    Description

    Callback URL if configured on the pipeline.

  • Name
    data.enrollmentToken
    Type
    string
    Description

    Optional. Present only when passkeys are enabled for both the account and the pipeline. A single-use, short-lived (~2 min) token you hand to the passkey enroll ceremony to offer the user a passkey right after this successful verify. Absent otherwise — treat its presence as "you may offer enrollment now." See V1.2 Passkeys.

  • Name
    message
    Type
    string
    Description

    "OTP verified successfully".

Failed Response

  • Name
    status
    Type
    string
    Description

    "error".

  • Name
    code
    Type
    string
    Description

    Error code: "INVALID_OTP" (403), "MAX_ATTEMPTS_EXCEEDED" (429), "TRANSACTION_EXPIRED" (410), or "ALREADY_VERIFIED" (409).

  • Name
    message
    Type
    string
    Description

    Error description.


Webhook Callbacks

When a transaction completes — successfully or not — Akedly POSTs the legacy verification-result JSON payload to the backendCallbackURL configured on your pipeline. This is how your server learns the result without polling. The same URL can also receive the separate signed OTP channel events described in OTP channel events.

V1.2 verification-result callbacks are signed

The direct V1.2 verification-result callback body contains mainTransaction and transactionReq. Dev-mode state is carried on those transaction objects. Verify the Svix signature on the raw body before acting; if a result ever arrives unsigned, confirm it with a server-side check on transactionID instead. This contract covers the verification-result callback; signed otp.channels.resolved and otp.channels.updated events use the separate signed channel-event contract.

  • Name
    Payload
    Type
    object
    Description
    The callback is an object with sanitized mainTransaction and the transactionReq object; it has no top-level status envelope.
  • Name
    Dev-mode marker
    Type
    boolean fields
    Description
    For a test-pair transaction, mainTransaction.isTest and transactionReq.isTest are true.
  • Name
    Signature
    Type
    svix headers, best effort
    Description
    Signed with the pipeline webhook signing secret, with svix-id, svix-timestamp and svix-signature headers. Retries reuse the same svix-id. If signing fails the result is still sent, without those headers.
  • Name
    OTP channel events
    Type
    excluded for test transactions
    Description
    A transaction marked isTest: true still receives this verification-result callback, but it does not emit signed otp.channels.resolved or otp.channels.updated events.

The delivery channel is in mainTransaction.resolvedChannel (the channel the code ended up on) and mainTransaction.channels (every channel used). See Where is the delivery channel? and the full payload reference.

The callback destination lives in the pipeline's Webhooks section. The verification-result callback is signed with the pipeline's signing secret (svix-id, svix-timestamp, svix-signature headers) — verify the raw body before acting on it. Signing is best effort: if a result ever arrives without those headers, confirm it through the API instead of rejecting it. The same URL also receives signed channel events; route them using the OTP channel event guide, and see Verify in your stack for the signature check.

To confirm a result that is late, missing or unsigned, use a transaction status lookup with the mainTransaction.transactionID you stored. The passkey ceremony poll, GET /api/v1.2/transactions/passkey/result, is a separate call authorized by the ceremony token. See the passkey endpoint reference. On a passkey sign-in the V1.2 webhook stamps its send time, so a date in a lookup can differ from the date in the webhook.