> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Receive a provider webhook

> Accepts a settlement event pushed by the payment provider, persists it verbatim and returns 202 only after that persistence succeeds. Under multi-tenancy, if the resolved tenant's storage is unavailable the event is not persisted and the response is 503 (retryable) instead of 202. Authentication is an HMAC-SHA256 signature over the raw request body, sent as lowercase hex in x-webhook-baas-signature and computed with a secret agreed with the provider, not the platform identity used by the rest of this API. The body is the provider envelope: pactualId, entity and status are required, clientRequestId and createTimestamp are optional, and any further member is accepted and stored as sent. Member names are matched without regard to case. The x-webhook-baas-entity and x-webhook-baas-event headers are optional copies of entity and status; where a header disagrees with the body the body is authoritative, because only the body is signed. The x-webhook-baas-notifyid header identifies the logical notification and, when sent, is what this endpoint deduplicates on; a delivery without it is still accepted and deduplicated on a key derived from pactualId, entity, status and createTimestamp. The x-webhook-baas-deliveryid header names one attempt to deliver that notification rather than the notification itself; it is optional, it decides nothing, and it is recorded only in this endpoint's logs, so that a delivery can be matched against the provider's own list of failed attempts. A delivery whose status names a provider TRANSPORT event rather than a business one — PING, which the provider's own webhook test emits and which belongs to no entity's event catalogue — is the single exception to all of the above: it is answered 202 without being deduplicated and without being persisted.



## OpenAPI

````yaml /en/openapi/v3-current/payments.yaml post /v1/webhooks/btg
openapi: 3.1.0
info:
  description: >-
    API for the Lerian Payments interface via BTG. It covers boleto issuance,
    cancellation, installments, and queries; bill payments (bankslip, utilities,
    and DARF), cancellation, and queries; aggregated boleto and payment
    dashboards; provider connection and outbound webhook configuration; and the
    provider webhook receiver for settlement events.


    ERROR BODIES. A failed request arrives in one of three shapes, and which one
    you get depends on where the service catches the failure, not on which
    endpoint you called. The authentication and authorization layer runs ahead
    of everything else and answers plain text with a bare reason and no error
    code. A failure that the request pipeline catches next, before the API layer
    sees it, answers a flat JSON body on application/json; the idempotency check
    is the pipeline rule you meet most often. Everything the API layer catches
    answers an RFC 9457 problem document on application/problem+json. Read the
    media type to tell the two JSON bodies apart. The code member holds the same
    PBP-NNNN value in both, so branch on it, and handle 401 and 403 by status
    because a refusal from the authentication layer carries no code. One
    pipeline refusal is not listed on the operations below: while a first
    request under the same idempotency key is still in flight, a retry is
    answered 409 with code PBP-0007 and the flat body.
  title: Payments — via BTG API
  version: 1.0.0
servers:
  - url: https://payments.sandbox.lerian.net
security: []
tags:
  - description: >-
      Tenant-level setup: connecting the banking provider and configuring where
      status notifications are delivered.
    name: Admin
  - description: >-
      Issuing, querying and cancelling boletos, including installment series and
      the rendered PDF.
    name: Boletos
  - description: >-
      Aggregated counts, amounts and breakdowns over a period, for boletos and
      for payments.
    name: Dashboards
  - description: >-
      Paying bankslips, utility bills and DARF tax slips, and querying or
      cancelling those payments.
    name: Payments
  - description: >-
      The webhook the payment provider posts settlement events to. Authenticated
      with a credential agreed with the provider, not with the platform identity
      used by the rest of this API.
    name: Settlement
paths:
  /v1/webhooks/btg:
    post:
      tags:
        - Settlement
      summary: Receive a provider webhook
      description: >-
        Accepts a settlement event pushed by the payment provider, persists it
        verbatim and returns 202 only after that persistence succeeds. Under
        multi-tenancy, if the resolved tenant's storage is unavailable the event
        is not persisted and the response is 503 (retryable) instead of 202.
        Authentication is an HMAC-SHA256 signature over the raw request body,
        sent as lowercase hex in x-webhook-baas-signature and computed with a
        secret agreed with the provider, not the platform identity used by the
        rest of this API. The body is the provider envelope: pactualId, entity
        and status are required, clientRequestId and createTimestamp are
        optional, and any further member is accepted and stored as sent. Member
        names are matched without regard to case. The x-webhook-baas-entity and
        x-webhook-baas-event headers are optional copies of entity and status;
        where a header disagrees with the body the body is authoritative,
        because only the body is signed. The x-webhook-baas-notifyid header
        identifies the logical notification and, when sent, is what this
        endpoint deduplicates on; a delivery without it is still accepted and
        deduplicated on a key derived from pactualId, entity, status and
        createTimestamp. The x-webhook-baas-deliveryid header names one attempt
        to deliver that notification rather than the notification itself; it is
        optional, it decides nothing, and it is recorded only in this endpoint's
        logs, so that a delivery can be matched against the provider's own list
        of failed attempts. A delivery whose status names a provider TRANSPORT
        event rather than a business one — PING, which the provider's own
        webhook test emits and which belongs to no entity's event catalogue — is
        the single exception to all of the above: it is answered 202 without
        being deduplicated and without being persisted.
      operationId: receiveProviderWebhook
      parameters:
        - description: >-
            HMAC-SHA256 of the raw request body under the secret agreed with the
            provider, lowercase hex. It is verified by a per-operation
            middleware that runs before any schema check, so a request that
            omits it, or carries a value that does not match the body, is
            refused with 401 PBP-0500 and never with a schema error.
          in: header
          name: x-webhook-baas-signature
          required: true
          schema:
            description: >-
              HMAC-SHA256 of the raw request body under the secret agreed with
              the provider, lowercase hex. It is verified by a per-operation
              middleware that runs before any schema check, so a request that
              omits it, or carries a value that does not match the body, is
              refused with 401 PBP-0500 and never with a schema error.
            type: string
        - description: >-
            Provider entity name, repeating the body's entity member.
            Cross-checked against it; the body decides, because only the body is
            under the signature.
          in: header
          name: x-webhook-baas-entity
          schema:
            description: >-
              Provider entity name, repeating the body's entity member.
              Cross-checked against it; the body decides, because only the body
              is under the signature.
            type: string
        - description: >-
            Provider event name, which is the same value as the body's status
            member. Cross-checked against it; the body decides.
          in: header
          name: x-webhook-baas-event
          schema:
            description: >-
              Provider event name, which is the same value as the body's status
              member. Cross-checked against it; the body decides.
            type: string
        - description: >-
            Provider identifier of the LOGICAL notification, as opposed to one
            delivery attempt of it. When present it becomes this delivery's
            deduplication key; when absent the key is derived from the envelope
            instead, and the delivery is still accepted.
          in: header
          name: x-webhook-baas-notifyid
          schema:
            description: >-
              Provider identifier of the LOGICAL notification, as opposed to one
              delivery attempt of it. When present it becomes this delivery's
              deduplication key; when absent the key is derived from the
              envelope instead, and the delivery is still accepted.
            type: string
        - description: >-
            Provider identifier of ONE ATTEMPT to deliver a notification, as
            opposed to the notification itself. It decides nothing: it is never
            deduplicated on and no column holds it, because an attempt
            identifier is fresh on every retry. It is recorded in this
            endpoint's logs so a delivery can be matched against the provider's
            own list of failed attempts. A delivery without it is still
            accepted.
          in: header
          name: x-webhook-baas-deliveryid
          schema:
            description: >-
              Provider identifier of ONE ATTEMPT to deliver a notification, as
              opposed to the notification itself. It decides nothing: it is
              never deduplicated on and no column holds it, because an attempt
              identifier is fresh on every retry. It is recorded in this
              endpoint's logs so a delivery can be matched against the
              provider's own list of failed attempts. A delivery without it is
              still accepted.
            type: string
        - description: >-
            Optional provider correlation identifier. Persisted with the event
            when present.
          in: header
          name: X-Correlation-Id
          schema:
            description: >-
              Optional provider correlation identifier. Persisted with the event
              when present.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProviderWebhookPayload'
        required: true
      responses:
        '202':
          description: Accepted
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Bad Request
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unauthorized
        '413':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Request Entity Too Large
        '415':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unsupported Media Type
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unprocessable Entity
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
            application/json:
              schema:
                $ref: '#/components/schemas/PipelineError'
          description: Internal Server Error
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Service Unavailable
components:
  schemas:
    ProviderWebhookPayload:
      additionalProperties: true
      properties:
        clientRequestId:
          description: >-
            Identifier this service sent when it created the entity, echoed back
            for correlation. Optional here even though the provider envelope
            declares it required.
          type: string
        createTimestamp:
          description: >-
            Instant the provider recorded for this state, RFC 3339. Optional
            here: a delivery without one means no reference instant, not a
            malformed request.
          format: date-time
          type: string
        entity:
          description: >-
            Provider entity name the event belongs to, for example
            PaymentSlipPay or TaxDarfPayment. Cross-checked against
            x-webhook-baas-entity; the body wins.
          type: string
        pactualId:
          description: >-
            Provider-assigned primary key of the entity this event is about. A
            string representation of a GUID in every delivery seen so far, but
            not validated as one: the field is the provider's to shape.
          type: string
        status:
          description: >-
            Provider state the entity moved to, for example PAID. This IS the
            provider's event name — the x-webhook-baas-event header carries the
            same value — and it is stored as sent.
          type: string
      required:
        - pactualId
        - entity
        - status
      type: object
    Detail:
      additionalProperties: false
      properties:
        code:
          description: >-
            Stable, machine-readable domain error code scoped to the emitting
            service (format: <SERVICE>-NNNN).
          examples:
            - ERR-0001
          type: string
        detail:
          description: >-
            A human-readable explanation specific to this occurrence of the
            problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: '#/components/schemas/ErrorDetail'
          type:
            - array
            - 'null'
        instance:
          description: >-
            A URI reference that identifies the specific occurrence of the
            problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: >-
            A short, human-readable summary of the problem type. This value
            should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
        upstream:
          $ref: '#/components/schemas/Upstream'
          description: >-
            RFC 9457 extension member: the error a proxied third-party provider
            reported. Absent unless the emitting service explicitly surfaced
            one.
      type: object
    PipelineError:
      additionalProperties: false
      description: >-
        The flat error body. A failure caught before the API layer sees the
        request answers this shape on the application/json media type, instead
        of the RFC 9457 problem document. Read the media type to tell the two
        apart. The idempotency check is the rule that answers this way most
        often.
      properties:
        code:
          description: >-
            Stable, machine-readable error code, in the form PBP-NNNN. Branch on
            this value. It carries the same meaning as the code member of the
            problem document.
          examples:
            - PBP-0013
          type: string
        details:
          additionalProperties: true
          description: Optional object carrying further facts about this occurrence.
          type:
            - object
            - 'null'
        message:
          description: A human-readable explanation specific to this occurrence.
          examples:
            - >-
              The request body does not match the original request for this
              idempotency key.
          type: string
        title:
          description: Short label for the condition.
          examples:
            - Idempotency Key Conflict
          type: string
      required:
        - code
        - title
        - message
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: >-
            Where the error occurred, e.g. 'body.items[3].tags' or
            'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
    Upstream:
      additionalProperties: false
      properties:
        code:
          description: The upstream provider's own error code, verbatim.
          examples:
            - E4001
          type: string
        message:
          description: >-
            The upstream provider's own error message, verbatim (bounded, never
            its raw response body).
          examples:
            - account not found at provider
          type: string
      type: object

````