> ## 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, and queries; bill payments (bankslip, utilities, and DARF) 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. Branch on the code member. A
    license refusal carries an LCS code, listed in the License Manager error
    list.
  title: Payments — via BTG API
  version: 1.0.0
servers:
  - url: https://api.example.com
    description: >-
      Replace the example host with the URL provided during environment
      onboarding.
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, and retrieving 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 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. Optional. The organization context is
            determined by the credentials you authenticate with; this header
            does not select or change it.
          in: header
          name: X-Organization-Id
          schema:
            description: >-
              Tenant organization ID. Optional. The organization context is
              determined by the credentials you authenticate with; this header
              does not select or change it.
            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: the request carries no valid credential, or
            the credential does not identify a tenant. The error body is either
            `application/json` with error code `PBP-0003` or
            `application/problem+json` (RFC 9457); read the `Content-Type` to
            parse it. The `text/plain` media type listed for this status is not
            returned. Obtain a valid bearer token and send the request again.
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
            text/plain:
              schema:
                type: string
          description: >-
            The authenticated principal is not allowed to perform this request.
            The error body is `application/json` with error code `PBP-0004`: the
            principal lacks permission for this operation, and the permission
            has to be granted before the request can succeed. The `text/plain`
            media type listed for this status is not returned.
        '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/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Internal Server Error
        '502':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Bad Gateway
        '503':
          description: >-
            Service Unavailable. PBP-0008 means the tenant's storage could not
            be resolved. The request was not processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      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
    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

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.