Skip to content
Sections

Payment callbacks

Ingest a signed provider payment-attempt event

POST/payment-provider-webhooks/{providerKey}

Verifies and deduplicates a signed provider event, records its immutable payload evidence, and advances the matching payment attempt atomically. A failure reported after capture creates settlement-exception evidence. The simulator adapter is supported; certified provider adapters remain release gates. Hallify HMAC profile payment-provider uses HMAC-SHA256. Canonical input: RFC3339 timestamp + "." + key-sorted stable JSON. Send x-hallify-timestamp as rfc3339; timestamps outside ±300 seconds are stale. Send x-hallify-signature as v1=<lowercase hexadecimal>; comparison is timing-safe. Secret handling: Use the configured provider secret as UTF-8 text. Accepted Content-Type: application/json. Malformed input: A malformed signature returns 401. Stale input: A timestamp outside the 300-second window returns 401. Repeated delivery: The provider event ID and payload hash provide duplicate handling; conflicting reuse returns 409.

Authentication

x-hallify-signature: <HALLIFY_SIGNATURE>

HMAC-SHA256 signature credential. Each operation documents its exact protocol through x-hallify-hmac metadata and the operation description.

Connection guide

Parameters

providerKeyPath · Required

Stable payment-provider key selecting the webhook verification profile for this delivery.

string
Full definition
{
  "example": "simulator",
  "type": "string"
}
x-hallify-timestampHeader · Required

rfc3339 timestamp; accepted clock skew is ±300 seconds.

string
Full definition
{
  "type": "string",
  "example": "example"
}

Request body

Required

Payment attempt and provider event identifiers, provider status and occurrence time, plus optional external and masked-card metadata consumed as the signed payment event.

application/json

PaymentProviderWebhookDto
Full definition
{
  "$ref": "#/components/schemas/PaymentProviderWebhookDto"
}

Responses

200Returns whether the signed provider event was applied or recognized as an idempotent replay.
PaymentProviderWebhookResultDto
Full definition
{
  "$ref": "#/components/schemas/PaymentProviderWebhookResultDto"
}
Example response · 200 · application/json
{
  "attemptId": "ebc45c68-e765-435d-8f7f-7ef875ce04bf",
  "status": "PENDING",
  "revision": 1,
  "duplicate": true
}
400Provider key or payment webhook payload is invalid
TranslatedErrorDto
Full definition
{
  "$ref": "#/components/schemas/TranslatedErrorDto"
}
Example response · 400 · application/json
{
  "statusCode": 400,
  "error": "errors:http.badRequest",
  "code": "HTTP_BAD_REQUEST",
  "message": "errors:http.badRequest",
  "translationKey": "errors:http.badRequest"
}
401Payment webhook is unavailable or its HMAC signature is invalid or stale
TranslatedErrorDto
Full definition
{
  "$ref": "#/components/schemas/TranslatedErrorDto"
}
Example response · 401 · application/json
{
  "statusCode": 401,
  "error": "errors:http.unauthorized",
  "code": "HTTP_UNAUTHORIZED",
  "message": "errors:http.unauthorized",
  "translationKey": "errors:http.unauthorized"
}
404Payment attempt was not found
TranslatedErrorDto
Full definition
{
  "$ref": "#/components/schemas/TranslatedErrorDto"
}
Example response · 404 · application/json
{
  "statusCode": 404,
  "error": "errors:http.notFound",
  "code": "HTTP_NOT_FOUND",
  "message": "errors:http.notFound",
  "translationKey": "errors:http.notFound"
}
409Provider event identifier was reused with a different payload
TranslatedErrorDto
Full definition
{
  "$ref": "#/components/schemas/TranslatedErrorDto"
}
Example response · 409 · application/json
{
  "statusCode": 409,
  "error": "errors:http.conflict",
  "code": "HTTP_CONFLICT",
  "message": "errors:http.conflict",
  "translationKey": "errors:http.conflict"
}

Schemas

PaymentProviderWebhookDto

Signed payment-provider event payload after HMAC verification at the provider webhook boundary.

attemptIdRequired

Hallify UUID identifying attempt associated with payment provider webhook.

string · uuid
Full definition
{
  "type": "string",
  "format": "uuid",
  "description": "Hallify UUID identifying attempt associated with payment provider webhook.",
  "example": "ebc45c68-e765-435d-8f7f-7ef875ce04bf"
}
eventIdRequired

Opaque identifier for event associated with payment provider webhook; clients must not assume UUID syntax. Accepted values enforce maximum length 191.

string
Full definition
{
  "type": "string",
  "maxLength": 191,
  "description": "Opaque identifier for event associated with payment provider webhook; clients must not assume UUID syntax. Accepted values enforce maximum length 191.",
  "example": "example-id"
}
statusRequired

Current lifecycle status of payment provider webhook; allowed values are AUTHORIZED, CAPTURED, DECLINED, FAILED, CANCELED.

string
Full definition
{
  "type": "string",
  "enum": [
    "AUTHORIZED",
    "CAPTURED",
    "DECLINED",
    "FAILED",
    "CANCELED"
  ],
  "description": "Current lifecycle status of payment provider webhook; allowed values are AUTHORIZED, CAPTURED, DECLINED, FAILED, CANCELED.",
  "example": "AUTHORIZED"
}
occurredAtRequired

RFC 3339 timestamp for occurred at; offsets represent the same absolute instant.

string · date-time
Full definition
{
  "type": "string",
  "format": "date-time",
  "description": "RFC 3339 timestamp for occurred at; offsets represent the same absolute instant.",
  "example": "2026-01-15T12:00:00.000Z"
}
externalReferenceOptional

Reference assigned by an external system and retained for reconciliation with payment provider webhook. Accepted values enforce maximum length 128.

string
Full definition
{
  "type": "string",
  "maxLength": 128,
  "description": "Reference assigned by an external system and retained for reconciliation with payment provider webhook. Accepted values enforce maximum length 128.",
  "example": "example-reference"
}
cardBrandOptional

Card brand for payment provider webhook. Accepted values enforce maximum length 32.

string
Full definition
{
  "type": "string",
  "maxLength": 32,
  "description": "Card brand for payment provider webhook. Accepted values enforce maximum length 32.",
  "example": "example"
}
cardLast4Optional

Card last4 for payment provider webhook.

string
Full definition
{
  "type": "string",
  "pattern": "^\\d{4}$",
  "description": "Card last4 for payment provider webhook.",
  "example": "0001"
}
Full definition
{
  "type": "object",
  "properties": {
    "attemptId": {
      "type": "string",
      "format": "uuid",
      "description": "Hallify UUID identifying attempt associated with payment provider webhook.",
      "example": "ebc45c68-e765-435d-8f7f-7ef875ce04bf"
    },
    "eventId": {
      "type": "string",
      "maxLength": 191,
      "description": "Opaque identifier for event associated with payment provider webhook; clients must not assume UUID syntax. Accepted values enforce maximum length 191.",
      "example": "example-id"
    },
    "status": {
      "type": "string",
      "enum": [
        "AUTHORIZED",
        "CAPTURED",
        "DECLINED",
        "FAILED",
        "CANCELED"
      ],
      "description": "Current lifecycle status of payment provider webhook; allowed values are AUTHORIZED, CAPTURED, DECLINED, FAILED, CANCELED.",
      "example": "AUTHORIZED"
    },
    "occurredAt": {
      "type": "string",
      "format": "date-time",
      "description": "RFC 3339 timestamp for occurred at; offsets represent the same absolute instant.",
      "example": "2026-01-15T12:00:00.000Z"
    },
    "externalReference": {
      "type": "string",
      "maxLength": 128,
      "description": "Reference assigned by an external system and retained for reconciliation with payment provider webhook. Accepted values enforce maximum length 128.",
      "example": "example-reference"
    },
    "cardBrand": {
      "type": "string",
      "maxLength": 32,
      "description": "Card brand for payment provider webhook. Accepted values enforce maximum length 32.",
      "example": "example"
    },
    "cardLast4": {
      "type": "string",
      "pattern": "^\\d{4}$",
      "description": "Card last4 for payment provider webhook.",
      "example": "0001"
    }
  },
  "required": [
    "attemptId",
    "eventId",
    "status",
    "occurredAt"
  ],
  "description": "Signed payment-provider event payload after HMAC verification at the provider webhook boundary.",
  "example": {
    "attemptId": "fc6f0fa9-b807-43e6-87b6-56edb271fb3f",
    "eventId": "evt_01JHALLIFYEXAMPLE",
    "status": "CAPTURED",
    "occurredAt": "2026-08-11T10:30:00.000Z",
    "externalReference": "provider-payment-1842",
    "cardBrand": "visa",
    "cardLast4": "4242"
  }
}
PaymentProviderWebhookResultDto

Returns whether the signed provider event was applied or recognized as an idempotent replay.

attemptIdRequired

Hallify UUID identifying attempt associated with payment provider webhook.

string · uuid
Full definition
{
  "type": "string",
  "format": "uuid",
  "description": "Hallify UUID identifying attempt associated with payment provider webhook.",
  "example": "ebc45c68-e765-435d-8f7f-7ef875ce04bf"
}
statusRequired

Current lifecycle status of payment provider webhook; allowed values are PENDING, APPROVED, FAILED, CANCELED, CREATED, REQUIRES_ACTION, AUTHORIZED, CAPTURED, DECLINED, OFFLINE_ACCEPTED, RECONCILIATION_REQUIRED.

string
Full definition
{
  "type": "string",
  "enum": [
    "PENDING",
    "APPROVED",
    "FAILED",
    "CANCELED",
    "CREATED",
    "REQUIRES_ACTION",
    "AUTHORIZED",
    "CAPTURED",
    "DECLINED",
    "OFFLINE_ACCEPTED",
    "RECONCILIATION_REQUIRED"
  ],
  "description": "Current lifecycle status of payment provider webhook; allowed values are PENDING, APPROVED, FAILED, CANCELED, CREATED, REQUIRES_ACTION, AUTHORIZED, CAPTURED, DECLINED, OFFLINE_ACCEPTED, RECONCILIATION_REQUIRED.",
  "example": "PENDING"
}
revisionRequired

Monotonic optimistic-concurrency revision for payment provider webhook.

integer
Full definition
{
  "type": "integer",
  "description": "Monotonic optimistic-concurrency revision for payment provider webhook.",
  "example": 1
}
duplicateRequired

Whether the submitted event was already accepted and handled as a duplicate.

boolean
Full definition
{
  "type": "boolean",
  "description": "Whether the submitted event was already accepted and handled as a duplicate.",
  "example": true
}
Full definition
{
  "type": "object",
  "properties": {
    "attemptId": {
      "type": "string",
      "format": "uuid",
      "description": "Hallify UUID identifying attempt associated with payment provider webhook.",
      "example": "ebc45c68-e765-435d-8f7f-7ef875ce04bf"
    },
    "status": {
      "type": "string",
      "enum": [
        "PENDING",
        "APPROVED",
        "FAILED",
        "CANCELED",
        "CREATED",
        "REQUIRES_ACTION",
        "AUTHORIZED",
        "CAPTURED",
        "DECLINED",
        "OFFLINE_ACCEPTED",
        "RECONCILIATION_REQUIRED"
      ],
      "description": "Current lifecycle status of payment provider webhook; allowed values are PENDING, APPROVED, FAILED, CANCELED, CREATED, REQUIRES_ACTION, AUTHORIZED, CAPTURED, DECLINED, OFFLINE_ACCEPTED, RECONCILIATION_REQUIRED.",
      "example": "PENDING"
    },
    "revision": {
      "type": "integer",
      "description": "Monotonic optimistic-concurrency revision for payment provider webhook.",
      "example": 1
    },
    "duplicate": {
      "type": "boolean",
      "description": "Whether the submitted event was already accepted and handled as a duplicate.",
      "example": true
    }
  },
  "required": [
    "attemptId",
    "status",
    "revision",
    "duplicate"
  ],
  "description": "Returns whether the signed provider event was applied or recognized as an idempotent replay.",
  "example": {
    "attemptId": "ebc45c68-e765-435d-8f7f-7ef875ce04bf",
    "status": "PENDING",
    "revision": 1,
    "duplicate": true
  }
}
TranslatedErrorDto

Stable error envelope emitted by the global HTTP exception boundary. Domain-specific machine data, when present, is nested under details.

statusCodeRequired

HTTP status code repeated from the response.

integer
Full definition
{
  "type": "integer",
  "description": "HTTP status code repeated from the response.",
  "example": 400
}
errorRequired

Translation key for the HTTP category, such as errors:http.conflict. The specific cause is identified by code and translationKey.

string
Full definition
{
  "type": "string",
  "description": "Translation key for the HTTP category, such as errors:http.conflict. The specific cause is identified by code and translationKey.",
  "example": "errors.request.invalid"
}
codeRequired

Stable machine-readable domain code, or an HTTP_* fallback when no domain code is provided. Branch on this field and the HTTP status; localized wording never controls retries or business decisions.

string
Full definition
{
  "type": "string",
  "example": "HTTP_BAD_REQUEST",
  "description": "Stable machine-readable domain code, or an HTTP_* fallback when no domain code is provided. Branch on this field and the HTTP status; localized wording never controls retries or business decisions."
}
messageRequired

Translation key, identical to translationKey. The API does not return localized display text. Resolve the key and translationValues in the consuming application.

string
Full definition
{
  "type": "string",
  "example": "errors:http.badRequest",
  "description": "Translation key, identical to translationKey. The API does not return localized display text. Resolve the key and translationValues in the consuming application."
}
translationKeyRequired

Canonical translation key, identical to message. Status-specific errors:http.* keys cover unspecified failures. Public keys and EN/RU explanations are listed in Developers; unknown keys require a localized client fallback.

string
Full definition
{
  "type": "string",
  "example": "errors:http.badRequest",
  "description": "Canonical translation key, identical to message. Status-specific errors:http.* keys cover unspecified failures. Public keys and EN/RU explanations are listed in Developers; unknown keys require a localized client fallback."
}
translationValuesOptional

Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.

object
Full definition
{
  "type": "object",
  "additionalProperties": {
    "oneOf": [
      {
        "type": "string",
        "example": "example"
      },
      {
        "type": "number",
        "example": 0
      },
      {
        "type": "boolean",
        "example": true
      }
    ],
    "example": "example"
  },
  "description": "Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.",
  "example": {
    "exampleKey": "example"
  }
}
validationErrorsOptional

Field validation failures with translation keys and stable validator codes. Submitted values and raw validator text are not included.

array
Full definition
{
  "description": "Field validation failures with translation keys and stable validator codes. Submitted values and raw validator text are not included.",
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/ValidationErrorDto"
  },
  "example": [
    {
      "field": "email",
      "translationKey": "validation:isEmail",
      "code": "isEmail",
      "message": "validation:isEmail"
    }
  ]
}
detailsOptional

Optional domain-specific blocker or validation details

oneOf
Full definition
{
  "description": "Optional domain-specific blocker or validation details",
  "oneOf": [
    {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/JsonValue"
      },
      "example": [
        "example"
      ]
    },
    {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/components/schemas/JsonValue"
      },
      "example": {
        "exampleKey": "example"
      }
    }
  ],
  "example": [
    "example"
  ]
}
Full definition
{
  "type": "object",
  "properties": {
    "statusCode": {
      "type": "integer",
      "description": "HTTP status code repeated from the response.",
      "example": 400
    },
    "error": {
      "type": "string",
      "description": "Translation key for the HTTP category, such as errors:http.conflict. The specific cause is identified by code and translationKey.",
      "example": "errors.request.invalid"
    },
    "code": {
      "type": "string",
      "example": "HTTP_BAD_REQUEST",
      "description": "Stable machine-readable domain code, or an HTTP_* fallback when no domain code is provided. Branch on this field and the HTTP status; localized wording never controls retries or business decisions."
    },
    "message": {
      "type": "string",
      "example": "errors:http.badRequest",
      "description": "Translation key, identical to translationKey. The API does not return localized display text. Resolve the key and translationValues in the consuming application."
    },
    "translationKey": {
      "type": "string",
      "example": "errors:http.badRequest",
      "description": "Canonical translation key, identical to message. Status-specific errors:http.* keys cover unspecified failures. Public keys and EN/RU explanations are listed in Developers; unknown keys require a localized client fallback."
    },
    "translationValues": {
      "type": "object",
      "additionalProperties": {
        "oneOf": [
          {
            "type": "string",
            "example": "example"
          },
          {
            "type": "number",
            "example": 0
          },
          {
            "type": "boolean",
            "example": true
          }
        ],
        "example": "example"
      },
      "description": "Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.",
      "example": {
        "exampleKey": "example"
      }
    },
    "validationErrors": {
      "description": "Field validation failures with translation keys and stable validator codes. Submitted values and raw validator text are not included.",
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ValidationErrorDto"
      },
      "example": [
        {
          "field": "email",
          "translationKey": "validation:isEmail",
          "code": "isEmail",
          "message": "validation:isEmail"
        }
      ]
    },
    "details": {
      "description": "Optional domain-specific blocker or validation details",
      "oneOf": [
        {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/JsonValue"
          },
          "example": [
            "example"
          ]
        },
        {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/components/schemas/JsonValue"
          },
          "example": {
            "exampleKey": "example"
          }
        }
      ],
      "example": [
        "example"
      ]
    }
  },
  "required": [
    "statusCode",
    "error",
    "code",
    "message",
    "translationKey"
  ],
  "description": "Stable error envelope emitted by the global HTTP exception boundary. Domain-specific machine data, when present, is nested under details.",
  "example": {
    "statusCode": 400,
    "error": "errors:http.badRequest",
    "code": "HTTP_BAD_REQUEST",
    "message": "errors:http.badRequest",
    "translationKey": "errors:http.badRequest"
  }
}
ValidationErrorDto

Field validation failure with a field path, stable validator code and a translation key. Human-readable text belongs to the consuming application.

fieldRequired

Public field path. Nested properties and array indices are separated by dots.

string
Full definition
{
  "type": "string",
  "example": "email",
  "description": "Public field path. Nested properties and array indices are separated by dots."
}
translationKeyRequired

Translation key for this validator, identical to message. Resolve it using a client dictionary; see the public Developers error catalog.

string
Full definition
{
  "type": "string",
  "example": "validation:isEmail",
  "description": "Translation key for this validator, identical to message. Resolve it using a client dictionary; see the public Developers error catalog."
}
codeRequired

Stable validator identifier for this field failure. Custom validators may define additional identifiers.

string
Full definition
{
  "type": "string",
  "example": "isEmail",
  "description": "Stable validator identifier for this field failure. Custom validators may define additional identifiers."
}
messageRequired

Translation key describing this field validation failure, identical to translationKey. Submitted values and validator prose are not included.

string
Full definition
{
  "type": "string",
  "example": "validation:isEmail",
  "description": "Translation key describing this field validation failure, identical to translationKey. Submitted values and validator prose are not included."
}
translationValuesOptional

Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.

object
Full definition
{
  "type": "object",
  "additionalProperties": {
    "oneOf": [
      {
        "type": "string",
        "example": "example"
      },
      {
        "type": "number",
        "example": 0
      },
      {
        "type": "boolean",
        "example": true
      }
    ],
    "example": "example"
  },
  "description": "Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.",
  "example": {
    "exampleKey": "example"
  }
}
Full definition
{
  "type": "object",
  "properties": {
    "field": {
      "type": "string",
      "example": "email",
      "description": "Public field path. Nested properties and array indices are separated by dots."
    },
    "translationKey": {
      "type": "string",
      "example": "validation:isEmail",
      "description": "Translation key for this validator, identical to message. Resolve it using a client dictionary; see the public Developers error catalog."
    },
    "code": {
      "type": "string",
      "example": "isEmail",
      "description": "Stable validator identifier for this field failure. Custom validators may define additional identifiers."
    },
    "message": {
      "type": "string",
      "example": "validation:isEmail",
      "description": "Translation key describing this field validation failure, identical to translationKey. Submitted values and validator prose are not included."
    },
    "translationValues": {
      "type": "object",
      "additionalProperties": {
        "oneOf": [
          {
            "type": "string",
            "example": "example"
          },
          {
            "type": "number",
            "example": 0
          },
          {
            "type": "boolean",
            "example": true
          }
        ],
        "example": "example"
      },
      "description": "Optional scalar values for placeholders in the translation. Treat values as data, escape them when rendering, and never use them as translation options.",
      "example": {
        "exampleKey": "example"
      }
    }
  },
  "required": [
    "field",
    "translationKey",
    "code",
    "message"
  ],
  "description": "Field validation failure with a field path, stable validator code and a translation key. Human-readable text belongs to the consuming application.",
  "example": {
    "field": "email",
    "translationKey": "validation:isEmail",
    "code": "isEmail",
    "message": "validation:isEmail"
  }
}
JsonValue

Recursively JSON-safe value used only where the owning contract intentionally allows free-form structured data.

Full definition
{
  "oneOf": [
    {
      "type": "string",
      "nullable": true,
      "example": "example"
    },
    {
      "type": "number",
      "example": 0
    },
    {
      "type": "boolean",
      "example": true
    },
    {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/JsonValue"
      },
      "example": [
        "example"
      ]
    },
    {
      "type": "object",
      "additionalProperties": {
        "$ref": "#/components/schemas/JsonValue"
      },
      "example": {
        "exampleKey": "example"
      }
    }
  ],
  "description": "Recursively JSON-safe value used only where the owning contract intentionally allows free-form structured data.",
  "example": "example"
}

Hallify uses essential cookies and optional analytics.

Essential cookies keep sign-in, locale, and theme preferences working. Analytics is off until you choose to allow it.