Webhook notifications [Payouts API]

Monitor the status of payouts and top-ups using webhooks, and validate webhook signatures.

Payouts API will notify you of the following events using webhooks:

Details on the overall structure of webhooks, as well as their payload schemas, are below.

Webhook retry policy

We consider a webhook as having been successfully delivered when we receive a success status code (2xx) from the webhook URI you specified in your Console settings for Payouts API.

If we receive any other status code (for example, your API is temporarily unavailable), we will start retrying.

Our retry policy is jittered exponential backoff: we will immediately perform some fast retries and then start waiting increasingly longer; we will keep retrying for up to 72 hours.

Webhook structure

All webhooks from the Payouts API are structured in the same way.

In the header, you will see the following fields:

FieldTypeDescription
X-TL-Webhook-TimestamptimestampTime that the webhook was sent to you. This will be in the following format: 2020-05-18T10:17:47Z
TL-SignaturestringJSON web signature with a detached payload of the form {HEADER}..{SIGNATURE}

In the body, you will see the following fields:

FieldTypeDescription
event_typestringDescribing which event is detailed.
event_schema_versionunsigned integerThe version of the event body schema (for example, 1).
event_iduuidUnique ID of the event.
event_bodyobjectContaining the object relating to the event. The body schema is specified in the following sections for each event type.

Payout Authorised

The event body contains the following fields:

FieldTypeDescription
transaction_iduuidThe unique ID of the payout.
authorised_atTimestampThe date and time that the payout was authorised at.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "payout_authorised",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
     "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
     "authorised_at": "2019-10-01T17:00:00.0000000Z",
  }
}

Payout Submitted

The event body contains the following fields:

FieldTypeDescription
transaction_iduuidThe unique ID of the payout.
submitted_atTimestampThe date and time that the payout was submitted to the scheme.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "payout_submitted",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
     "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
     "submitted_at": "2019-10-01T17:00:00.0000000Z",
  }
}

Payout Settled

The event body contains the following fields:

FieldTypeDescription
transaction_iduuidThe unique ID of the payout.
settled_atTimestampThe date and time that the payout was settled into the beneficiary account.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "payout_settled",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
     "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
     "settled_at": "2019-10-01T17:00:00.0000000Z",
  }
}

Payout Rejected

The event body contains the following fields:

FieldTypeDescription
transaction_iduuidThe unique ID of the payout.
rejected_atTimestampThe date and time that the payout was rejected.
rejection_codeStringThe reason the payout was rejected as a machine-parsable enum.
rejection_detailsStringThe reason the payout was rejected as a human-friendly description.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "payout_rejected",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
     "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
     "rejected_at": "2019-10-01T18:00:00.0000000Z",
     "rejection_code": "insufficient_funds",
     "rejection_details": "There were not enough funds on the account to authorise the payout.",
  }
}

Payout Failed

The event body contains the following fields:

FieldTypeDescription
transaction_iduuidThe unique ID of the payout.
failed_atTimestampThe date and time that the payout status moved to failed.
failure_codeStringThe reason the payout as failed a machine-parsable enum.
failure_detailsStringThe reason the payout failed as a human-friendly description.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "payout_failed",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
     "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
     "failed_at": "2019-10-01T18:00:00.0000000Z",
     "failure_code": "server_error",
     "failure_details": "We encountered a technical issue while processing your payment, please try again later.",
  }
}

Top-up received

In the event body you will see the following fields:

FieldTypeDescription
transaction_iduuidunique ID of the transaction.
settled_atTimestampThe date and time the top-up was settled into the account.
amount_in_minorintegerThe amount received in the smallest denomination of the currency of the account.
currencyISO 4217 Currency Code StringThe currency the top-up was settled in.
remitter_ibanstringThe IBAN of the account that sent the funds to the account.
remitter_namestringThe name of the account holder that sent the funds to the account.
referencestringThe reference that will appear in your transaction history associated with this top-up.
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z

{
  "event_type": "topup_received",
  "event_id":  "33c9fc5b-69d7-4de0-83a9-8177f9af79d2",
  "event_schema_version": 1,
  "event_body": {
      "transaction_id": "cc328607-e02e-49e2-81c9-5bd044c8f7d7",
      "settled_at": "2019-10-01T17:00:00.0000000Z",
      "amount_in_minor": 10000,
      "currency": "GBP",
      "remitter_iban": "GB33BUKB20201555555555",
      "remitter_name": "Payment Corp"
      "reference": "Payment Corp Topup"
  }
}

Validate the received webhook signature

We recommend developers to use our signing libraries to verify the Tl-Signature of the received webhooks.

Java com.truelayer.truelayer-signing

Verifier.verifyWithJwks(jwks)
        .method("POST")
        .path(path)
        .headers(allWebhookHeaders)
        .body(body)
        .verify(webhookSignature);