> ## 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 immediately runs the provisioning saga that makes it usable: the ISPB is checked for uniqueness (and, on a tenant with JD directory validation enabled, for existence as an SPI participant at JD), THE PLUGIN creates the participant's `@pi_{ispb}` settlement account in Midaz — a client never creates it — and the row flips to ACTIVE. Call it once per indirect you onboard, before any inbound credit or outbound order names it: resolution is ACTIVE-gated, so a row still in PENDING_PROVISIONING is neither routable inbound nor usable as an outbound source.

THIS WRITE IS PERMANENT. 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.

The saga runs inline but does NOT gate the 201. When a step fails the response is still 201, carrying status PENDING_PROVISIONING and provisioning.failedStep (validateISPB, createPIAccount or markActive); re-run it with POST /v1/indirects/{indirectId}/provisioning/retry. So read status off the response rather than assuming ACTIVE.

Refusals: 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, or an absent ispb (the `@pi_{ispb}` alias derives from it, so the MVP requires one); 503 PIX-0107 when the tenant's delivery-secret encryption is unavailable — refused before the insert, so nothing was stored.



## 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
        immediately runs the provisioning saga that makes it usable: the ISPB is
        checked for uniqueness (and, on a tenant with JD directory validation
        enabled, for existence as an SPI participant at JD), THE PLUGIN creates
        the participant's `@pi_{ispb}` settlement account in Midaz — a client
        never creates it — and the row flips to ACTIVE. Call it once per
        indirect you onboard, before any inbound credit or outbound order names
        it: resolution is ACTIVE-gated, so a row still in PENDING_PROVISIONING
        is neither routable inbound nor usable as an outbound source.


        THIS WRITE IS PERMANENT. 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.


        The saga runs inline but does NOT gate the 201. When a step fails the
        response is still 201, carrying status PENDING_PROVISIONING and
        provisioning.failedStep (validateISPB, createPIAccount or markActive);
        re-run it with POST /v1/indirects/{indirectId}/provisioning/retry. So
        read status off the response rather than assuming ACTIVE.


        Refusals: 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, or an
        absent ispb (the `@pi_{ispb}` alias derives from it, so the MVP requires
        one); 503 PIX-0107 when the tenant's delivery-secret encryption is
        unavailable — refused before the insert, so nothing was stored.
      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
        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: Provisioning-saga state.
        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: The saga step that last failed, or null when clean.
          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

````