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

# Register an indirect participant

> Registers an indirect participant in this tenant's registry and makes it usable in one step. Call it once per indirect you onboard, before any inbound credit or outbound order names it.

REGISTRATION IS ATOMIC: a 201 means the participant is READY, and a failure means nothing was created. Before writing anything the service checks the ispb is not already held by a non-CLOSED indirect, optionally checks (on a tenant with JD directory validation enabled) that it exists as an SPI participant at JD, and creates the participant's `@pi_{ispb}` settlement account in Midaz — THE PLUGIN creates that account, a client never does. Only then is the row written, and it is written ACTIVE, so there is no intermediate state to poll and no `provisioning` field to read on the response. If any of those steps fails you get a coded error naming the step, NO registry row exists, and the ispb is still free: fix the cause and POST again. There is no retry endpoint, because there is nothing half-written to resume.

THIS WRITE IS PERMANENT once it succeeds. There is no DELETE on /v1/indirects: the registry's only exit is PATCH with action `close`, CLOSED is terminal (nothing leaves it), and even a close keeps the row and its `@pi` account — the account is blocked, never deleted. An indirect registered by mistake against a live tenant stays in that tenant's registry, and its ispb is held against the open-uniqueness constraint until it is closed. Verify name, ispb and the delivery endpoint before calling.

Refusals, all of which leave the registry untouched: 409 PIX-0093 when a non-CLOSED indirect already holds this ispb — a CLOSED one may be re-registered and reuses its historical `@pi` account; 422 PIX-0098 for a missing or over-120-character name, an ispb that is not exactly 8 digits, a delivery.endpointUrl that is not a valid https URL, a missing delivery.secret, an unknown messagingMode, an absent ispb (the `@pi_{ispb}` alias derives from it, so the MVP requires one), or — on a tenant with JD directory validation enabled — an ispb JD does not know as an SPI participant; 502 PIX-0099 when a step failed on a retryable fault, naming the step (validateISPB or createPIAccount) so you know which dependency to check before re-POSTing; 503 PIX-4002 when the ledger could not be reached to create the settlement account; 503 PIX-0107 when the tenant's delivery-secret encryption is unavailable.

Re-POSTing after a failure is safe: the settlement account is created by alias, so an attempt that got that far and then failed leaves an account the next attempt adopts rather than duplicating.



## OpenAPI

````yaml /en/openapi/v3-current/pix.yaml post /v1/indirects
openapi: 3.1.0
info:
  description: Brazilian PIX Direct (DICT + SPI) plugin API.
  title: plugin-br-pix-jd
  version: 1.0.0
servers: []
security:
  - BearerAuth: []
paths:
  /v1/indirects:
    post:
      tags:
        - Indirects
      summary: Register an indirect participant
      description: >-
        Registers an indirect participant in this tenant's registry and makes it
        usable in one step. Call it once per indirect you onboard, before any
        inbound credit or outbound order names it.


        REGISTRATION IS ATOMIC: a 201 means the participant is READY, and a
        failure means nothing was created. Before writing anything the service
        checks the ispb is not already held by a non-CLOSED indirect, optionally
        checks (on a tenant with JD directory validation enabled) that it exists
        as an SPI participant at JD, and creates the participant's `@pi_{ispb}`
        settlement account in Midaz — THE PLUGIN creates that account, a client
        never does. Only then is the row written, and it is written ACTIVE, so
        there is no intermediate state to poll and no `provisioning` field to
        read on the response. If any of those steps fails you get a coded error
        naming the step, NO registry row exists, and the ispb is still free: fix
        the cause and POST again. There is no retry endpoint, because there is
        nothing half-written to resume.


        THIS WRITE IS PERMANENT once it succeeds. There is no DELETE on
        /v1/indirects: the registry's only exit is PATCH with action `close`,
        CLOSED is terminal (nothing leaves it), and even a close keeps the row
        and its `@pi` account — the account is blocked, never deleted. An
        indirect registered by mistake against a live tenant stays in that
        tenant's registry, and its ispb is held against the open-uniqueness
        constraint until it is closed. Verify name, ispb and the delivery
        endpoint before calling.


        Refusals, all of which leave the registry untouched: 409 PIX-0093 when a
        non-CLOSED indirect already holds this ispb — a CLOSED one may be
        re-registered and reuses its historical `@pi` account; 422 PIX-0098 for
        a missing or over-120-character name, an ispb that is not exactly 8
        digits, a delivery.endpointUrl that is not a valid https URL, a missing
        delivery.secret, an unknown messagingMode, an absent ispb (the
        `@pi_{ispb}` alias derives from it, so the MVP requires one), or — on a
        tenant with JD directory validation enabled — an ispb JD does not know
        as an SPI participant; 502 PIX-0099 when a step failed on a retryable
        fault, naming the step (validateISPB or createPIAccount) so you know
        which dependency to check before re-POSTing; 503 PIX-4002 when the
        ledger could not be reached to create the settlement account; 503
        PIX-0107 when the tenant's delivery-secret encryption is unavailable.


        Re-POSTing after a failure is safe: the settlement account is created by
        alias, so an attempt that got that far and then failed leaves an account
        the next attempt adopts rather than duplicating.
      operationId: createIndirect
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIndirectBody'
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Indirect'
          description: Created
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Unprocessable Entity
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Internal Server Error
        default:
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Error
components:
  schemas:
    CreateIndirectBody:
      additionalProperties: false
      properties:
        delivery:
          $ref: '#/components/schemas/CreateIndirectDelivery'
          description: Delivery endpoint + signing secret.
        ispb:
          description: >-
            The indirect PSP's own ISPB (exactly 8 digits). Required for MVP
            flows.
          examples:
            - '12345678'
          type: string
        messagingMode:
          description: Delivery mode; the MVP supports 'raw' (JD-pure passthrough).
          examples:
            - raw
          type: string
        name:
          description: Display name of the indirect participant (1..120 chars).
          examples:
            - Indirect PSP Ltda
          type: string
      required:
        - name
      type: object
    Indirect:
      additionalProperties: false
      properties:
        createdAt:
          description: Creation timestamp (UTC).
          format: date-time
          type: string
        delivery:
          $ref: '#/components/schemas/IndirectDelivery'
          description: Delivery endpoint (secret redacted).
        indirectId:
          description: The indirect participant id (routing identifier).
          examples:
            - 018f2b7c-0000-7000-8000-000000000000
          type: string
        ispb:
          description: The indirect PSP's ISPB.
          examples:
            - '12345678'
          type: string
        messagingMode:
          description: Delivery mode.
          examples:
            - raw
          type: string
        name:
          description: Display name.
          examples:
            - Indirect PSP Ltda
          type: string
        piAccountAlias:
          description: The derived @pi_{ispb} Midaz account alias.
          examples:
            - '@pi_12345678'
          type: string
        provisioning:
          $ref: '#/components/schemas/IndirectProvisioning'
          description: >-
            Legacy provisioning state. Always present as an object; its
            failedStep is always null for a row registered by this service.
        qrCertificate:
          $ref: '#/components/schemas/IndirectQRCertificate'
          description: Own-QR-code certificate configuration.
        status:
          description: Lifecycle status.
          examples:
            - ACTIVE
          type: string
        updatedAt:
          description: Last-update timestamp (UTC).
          format: date-time
          type: string
      required:
        - indirectId
        - name
        - ispb
        - status
        - piAccountAlias
        - messagingMode
        - delivery
        - qrCertificate
        - provisioning
        - createdAt
        - updatedAt
      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
    CreateIndirectDelivery:
      additionalProperties: false
      properties:
        endpointUrl:
          description: HTTPS endpoint the indirect's events are delivered to.
          examples:
            - https://indirect.example.com/pix
          type: string
        secret:
          description: HMAC-SHA256 signing secret; encrypted at rest; never returned.
          examples:
            - s3cr3t
          type: string
      required:
        - endpointUrl
      type: object
    IndirectDelivery:
      additionalProperties: false
      properties:
        endpointUrl:
          description: The HTTPS delivery endpoint.
          examples:
            - https://indirect.example.com/pix
          type: string
        secret:
          description: Always redacted.
          examples:
            - '***'
          type: string
      required:
        - endpointUrl
        - secret
      type: object
    IndirectProvisioning:
      additionalProperties: false
      properties:
        failedStep:
          description: >-
            Legacy field: the registration step a PENDING_PROVISIONING row
            stopped at. Always null for an indirect registered by this service,
            since a failed registration leaves no row.
          examples:
            - createPIAccount
          type:
            - string
            - 'null'
      required:
        - failedStep
      type: object
    IndirectQRCertificate:
      additionalProperties: false
      properties:
        ownCertificate:
          description: >-
            Whether the indirect hosts the dynamic-QR JWS/JWKS under its own
            certificate. False means it falls back to the direct participant.
          examples:
            - true
          type: boolean
        publicBaseUrl:
          description: >-
            The indirect's scheme-less public base URL for QR payload locations.
            Empty when ownCertificate is false.
          examples:
            - qr.indirect.example.com/pix
          type: string
      required:
        - ownCertificate
        - publicBaseUrl
      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

````