> ## 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.

# Re-register the tenant's existing webhook subscription at a provider

> Re-registers this tenant's EXISTING webhook subscription at the provider, without rotating the connection.

WHAT IT DOES, EXACTLY: it reads the connection this tenant currently serves deliveries on, recomposes that connection's delivery URL, and sends the provider a subscription write carrying the declared resource set pointed at that URL. The connection record is untouched — same connection token, same signing key, same delivery URL — so a URL or a key you are already holding stays valid, and nothing you have configured elsewhere needs to change.

⚠️ WHAT IT DOES NOT DO, AND THE LIMIT IS THE POINT. When the provider reports a subscription as BLOCKED after repeated delivery failures, the procedure that returns it to service is NOT MEASURED by this service. This operation re-registers; whether the provider's consecutive-error counter resets, and whether deliveries resume, is something you OBSERVE afterwards — in the provider's own subscription state and in this service's webhook.subscription.blocked gauge — rather than something this call promises. Confirm the procedure with the provider before treating a blocked channel as repaired.

⛔ THE WRITE REPLACES THE PROVIDER'S RESOURCE ARRAY WHOLE, WHICH IS WHY IT READS BEFORE IT WRITES. The resources field of the answer is the entire set that is registered afterwards; any resource the provider held that is not in that list would no longer be subscribed, and the provider reports nothing about a removal. On a deployment where several consumers share one provider credential, that array is shared too, so the resource at risk may not even be yours. This operation therefore reads the subscription the provider currently holds under your credential BEFORE writing, and refuses with 409 if that subscription carries anything outside the set below — the answer then names resources that would have been removed, and nothing is written. The read exists only to refuse: the write's resource array is always the declared set and is never assembled from what the provider reports. If that read cannot be completed the call is refused with 502, because a read that failed says nothing about what the provider is holding.

⚠️ THERE IS ONE WAY PAST THAT 409, AND IT IS EXPLICIT: send the optional acknowledge_revoking naming exactly the resources the refusal reported. The call then proceeds and THOSE RESOURCES ARE REMOVED from the provider's subscription. The set is measured again on that call and compared against what you sent, so a name you left out — or a name the provider is not holding outside the declared set — is refused with another 409 saying which, and still writes nothing. It is NOT a conditional write: the provider offers none, so the window between this service reading the subscription and writing it is as open as it ever was. What the acknowledgement rules out is acting on a stale picture — a resource that appeared between the refusal you read and the call you sent makes your call FAIL instead of destroying something you never saw.

IT IS A LIVE TENANT'S SELF-SERVICE ACTION, AND THAT BOUNDS WHO YOU ACT AS, NOT WHAT YOU AFFECT. The tenant comes from the credentials you authenticate with and there is no tenant parameter anywhere in this request, so you cannot name somebody else. The provider, however, files ONE subscription per provider credential, not one per tenant: where several tenants share a credential, that single subscription is what this write re-points at YOUR tenant's delivery URL, and the others keep receiving deliveries only because their own health poller notices the registered address is not the one it composed and re-registers on its next tick. Which tenants share a credential is not something this service can see.

THE ANSWER CARRIES THE DELIVERY URL, WHICH IS A CREDENTIAL — the connection's routing token is a segment of it. Treat the response body as secret. It carries NO signing key.

A tenant with no connection to re-present, and a provider_type this deployment does not serve, are both refused with 404. The operation declares no Idempotency-Key: a retry re-executes, and re-executing sends the identical write.

⚠️ A RETRY OF A CALL THAT CARRIED acknowledge_revoking IS THE ONE EXCEPTION TO THAT, AND THE 409 IT ANSWERS PROVES LESS THAN IT LOOKS LIKE. Once a forced write has landed, the resources you acknowledged are gone from the provider's subscription — so on the retry they are no longer outside the declared set, your acknowledgement names something that is not there, and the call is refused with 409. That refusal reports one thing only: the set measured on this call is no longer the set you named. It is consistent with your first call having landed, and equally consistent with it never having reached the provider while some OTHER actor removed those resources in the meantime — on a shared provider credential, another consumer's own health poller is exactly such an actor. So it is not evidence of either on its own: do not read it as proof the write happened, and do not read it as "nothing was written" either. Check whether the removal already happened before re-sending, and re-send WITHOUT the field to see the current state. This is why a retry on a timeout should be a plain call, not a repeated force.



## OpenAPI

````yaml /en/openapi/v3-current/payments.yaml post /v1/admin/providers/webhooks/{provider_type}/resubscribe
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 two shapes, and which one
    you get depends on where the service catches the failure. A failure that the
    request pipeline catches 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. Authentication and authorization refusals follow the
    response contract documented by the operation. Everything the API layer
    catches answers an RFC 9457 problem document on application/problem+json.
    Read the media type to tell the two apart. The code member holds the same
    PBP-NNNN value in both, so branch on it. 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/admin/providers/webhooks/{provider_type}/resubscribe:
    post:
      tags:
        - Admin
      summary: Re-register the tenant's existing webhook subscription at a provider
      description: >-
        Re-registers this tenant's EXISTING webhook subscription at the
        provider, without rotating the connection.


        WHAT IT DOES, EXACTLY: it reads the connection this tenant currently
        serves deliveries on, recomposes that connection's delivery URL, and
        sends the provider a subscription write carrying the declared resource
        set pointed at that URL. The connection record is untouched — same
        connection token, same signing key, same delivery URL — so a URL or a
        key you are already holding stays valid, and nothing you have configured
        elsewhere needs to change.


        ⚠️ WHAT IT DOES NOT DO, AND THE LIMIT IS THE POINT. When the provider
        reports a subscription as BLOCKED after repeated delivery failures, the
        procedure that returns it to service is NOT MEASURED by this service.
        This operation re-registers; whether the provider's consecutive-error
        counter resets, and whether deliveries resume, is something you OBSERVE
        afterwards — in the provider's own subscription state and in this
        service's webhook.subscription.blocked gauge — rather than something
        this call promises. Confirm the procedure with the provider before
        treating a blocked channel as repaired.


        ⛔ THE WRITE REPLACES THE PROVIDER'S RESOURCE ARRAY WHOLE, WHICH IS WHY
        IT READS BEFORE IT WRITES. The resources field of the answer is the
        entire set that is registered afterwards; any resource the provider held
        that is not in that list would no longer be subscribed, and the provider
        reports nothing about a removal. On a deployment where several consumers
        share one provider credential, that array is shared too, so the resource
        at risk may not even be yours. This operation therefore reads the
        subscription the provider currently holds under your credential BEFORE
        writing, and refuses with 409 if that subscription carries anything
        outside the set below — the answer then names resources that would have
        been removed, and nothing is written. The read exists only to refuse:
        the write's resource array is always the declared set and is never
        assembled from what the provider reports. If that read cannot be
        completed the call is refused with 502, because a read that failed says
        nothing about what the provider is holding.


        ⚠️ THERE IS ONE WAY PAST THAT 409, AND IT IS EXPLICIT: send the optional
        acknowledge_revoking naming exactly the resources the refusal reported.
        The call then proceeds and THOSE RESOURCES ARE REMOVED from the
        provider's subscription. The set is measured again on that call and
        compared against what you sent, so a name you left out — or a name the
        provider is not holding outside the declared set — is refused with
        another 409 saying which, and still writes nothing. It is NOT a
        conditional write: the provider offers none, so the window between this
        service reading the subscription and writing it is as open as it ever
        was. What the acknowledgement rules out is acting on a stale picture — a
        resource that appeared between the refusal you read and the call you
        sent makes your call FAIL instead of destroying something you never saw.


        IT IS A LIVE TENANT'S SELF-SERVICE ACTION, AND THAT BOUNDS WHO YOU ACT
        AS, NOT WHAT YOU AFFECT. The tenant comes from the credentials you
        authenticate with and there is no tenant parameter anywhere in this
        request, so you cannot name somebody else. The provider, however, files
        ONE subscription per provider credential, not one per tenant: where
        several tenants share a credential, that single subscription is what
        this write re-points at YOUR tenant's delivery URL, and the others keep
        receiving deliveries only because their own health poller notices the
        registered address is not the one it composed and re-registers on its
        next tick. Which tenants share a credential is not something this
        service can see.


        THE ANSWER CARRIES THE DELIVERY URL, WHICH IS A CREDENTIAL — the
        connection's routing token is a segment of it. Treat the response body
        as secret. It carries NO signing key.


        A tenant with no connection to re-present, and a provider_type this
        deployment does not serve, are both refused with 404. The operation
        declares no Idempotency-Key: a retry re-executes, and re-executing sends
        the identical write.


        ⚠️ A RETRY OF A CALL THAT CARRIED acknowledge_revoking IS THE ONE
        EXCEPTION TO THAT, AND THE 409 IT ANSWERS PROVES LESS THAN IT LOOKS
        LIKE. Once a forced write has landed, the resources you acknowledged are
        gone from the provider's subscription — so on the retry they are no
        longer outside the declared set, your acknowledgement names something
        that is not there, and the call is refused with 409. That refusal
        reports one thing only: the set measured on this call is no longer the
        set you named. It is consistent with your first call having landed, and
        equally consistent with it never having reached the provider while some
        OTHER actor removed those resources in the meantime — on a shared
        provider credential, another consumer's own health poller is exactly
        such an actor. So it is not evidence of either on its own: do not read
        it as proof the write happened, and do not read it as "nothing was
        written" either. Check whether the removal already happened before
        re-sending, and re-send WITHOUT the field to see the current state. This
        is why a retry on a timeout should be a plain call, not a repeated
        force.
      operationId: resubscribeProviderWebhook
      parameters:
        - description: >-
            Tenant organization ID. Accepted but ignored: the tenant is
            determined by the credentials you authenticate with, so sending this
            header, or sending a different value in it, changes nothing.
          in: header
          name: X-Organization-Id
          schema:
            description: >-
              Tenant organization ID. Accepted but ignored: the tenant is
              determined by the credentials you authenticate with, so sending
              this header, or sending a different value in it, changes nothing.
            type: string
        - description: >-
            Provider whose subscription is being re-registered, for example BTG.
            Matched case-insensitively; a provider this deployment does not
            serve is refused with 404.
          in: path
          name: provider_type
          required: true
          schema:
            description: >-
              Provider whose subscription is being re-registered, for example
              BTG. Matched case-insensitively; a provider this deployment does
              not serve is refused with 404.
            examples:
              - BTG
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResubscribeWebhookSubscriptionRequest'
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResubscribeWebhookSubscriptionResponse'
          description: OK
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
            text/plain:
              schema:
                type: string
          description: >-
            Authentication failed. Read the `Content-Type`: the authentication
            middleware answers `application/json` carrying error code
            `PBP-0003`, and the in-service identity gate further down the chain
            answers `application/problem+json` (RFC 9457). The content map also
            declares `text/plain`, which NOTHING in this service produces. Do
            not write a client branch for it.
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
            text/plain:
              schema:
                type: string
          description: >-
            The authenticated principal is not permitted to perform this action.
            Read the `Content-Type`: the authorization middleware answers
            `application/json` carrying error code `PBP-0004`, meaning the
            principal may not perform this ACTION at all. On POST /v1/payments
            and POST /v1/payments/darf a second shape is reachable —
            `application/problem+json` (RFC 9457) carrying `PBP-0209` — and it
            means the opposite thing about the action: the principal MAY perform
            it, and the signed-in end user is not the registered holder of the
            account the request names. Every other operation still has the
            single `PBP-0004` shape. The content map also declares `text/plain`,
            which NOTHING in this service produces. Do not write a client branch
            for it.
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Not Found
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Conflict
        '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
        '502':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Bad Gateway
      security:
        - BearerAuth: []
components:
  schemas:
    ResubscribeWebhookSubscriptionRequest:
      additionalProperties: false
      properties:
        acknowledge_revoking:
          description: >-
            OPTIONAL, and it forces a write that would otherwise be refused.
            Name EXACTLY the resources the 409 reported, each one spelled as the
            refusal printed it — a bare entity name, or an entity/EVENT pair
            where only an event under a declared entity is at risk — and the
            re-registration proceeds and REMOVES them from the provider's
            subscription. The set is re-measured at the provider on this call
            and compared against what you sent: a name you left out, or a name
            the provider is not holding outside the declared set, is refused
            with another 409 that says which. Comparison is exact per name — no
            case folding, no trimming — and insensitive to order; repeats are
            collapsed. Omit the field, or send an empty list, to get the
            ordinary refusal. It is NOT a conditional write: the provider offers
            none, so it cannot close the window between this service's read and
            its write. What it closes is the window between the refusal you read
            and the call you sent.
          examples:
            - - PaymentSlip
              - PaymentSlipPay/SOME_EVENT
          items:
            type: string
          type:
            - array
            - 'null'
      type: object
    ResubscribeWebhookSubscriptionResponse:
      additionalProperties: false
      properties:
        provider_type:
          type: string
        resources:
          items:
            $ref: '#/components/schemas/ResubscribeWebhookSubscriptionResource'
          type:
            - array
            - 'null'
        status:
          type: string
        webhook_url:
          type: string
      required:
        - status
        - provider_type
        - webhook_url
        - resources
      type: object
    Detail:
      additionalProperties: true
      properties:
        code:
          description: >-
            Stable, machine-readable domain error code scoped to the emitting
            service (format: <SERVICE>-NNNN).
          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
    ErrorResponse:
      additionalProperties: false
      properties:
        code:
          type: string
        details:
          additionalProperties: {}
          type: object
        message:
          type: string
        title:
          type: string
      required:
        - code
        - title
        - message
      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
    ResubscribeWebhookSubscriptionResource:
      additionalProperties: false
      properties:
        entity:
          type: string
        event_names:
          items:
            type: string
          type:
            - array
            - 'null'
      required:
        - entity
        - event_names
      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
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: JWT bearer token issued by the identity provider.
      scheme: bearer
      type: http

````