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

# Upsert a plugin declaration

> Creates or replaces the access-manager declaration for the given plugin slug. Called by the product itself, at start-up, with its own M2M token: the token must be issued to the target application (app.ClientId == token azp) and that application must declare for the slug, else `403`.

The body has two sections and each is replaced ONLY when present, so a product can publish one without touching the other:
- the ACCESS section — `permissions`, `roles` and `m2m` together (a permission names the roles it grants to, so they are applied as one full sync: what the section no longer declares is removed). Served in single-tenant deployments only; in multi-tenant it answers `501`, because the tenant manager materializes permissions there.
- the SCOPE section — `scope.dimensions`, the dimensions the product's routes address instances by, and `partners`, the product's opt-in to partner credentials (either alone is the section), plus `levels` in the scope-only form. It replaces the product's scope catalog wholesale and is served in single- AND multi-tenant deployments: the catalog is global per product. Every partner write is validated against it, and `GET /v1/scope-catalog/{product}` reads it back.

A body carrying both sections in multi-tenant applies the scope section and then answers `501` for the access section.

Failures: `422` — the manifest is malformed (the message lists every violation), declares neither section, its `service` is not the slug, no application is registered for the calling client, or the identity provider refused the scope catalog (the message repeats its code, e.g. `CRDH-3120`); `403` — the token does not speak for the slug; `409` — the access section could not be persisted; `501` — the access section in a multi-tenant deployment; `503 IDE-0060` — the identity provider is unavailable, retry later; `500` — another dependency failed, retry.



## OpenAPI

````yaml /pt/openapi/v3-current/AM-identity.yaml put /v1/declarations/{slug}
openapi: 3.1.0
info:
  contact:
    name: Lerian Studio
    url: https://lerian.studio
  description: >-
    OpenAPI 3.1 surface for the plugin-access-manager identity component. It
    exposes the M2M-gated declarations upsert (PUT /v1/declarations/{slug}),
    through which each plugin declares its own permissions/roles/M2M contract,
    and partner management (/v1/partners), through which a tenant administrator
    grants its own customers scoped API credentials. The remaining identity
    routes stay Fiber-native and are described by the separate OAS 2 document in
    the same folder.
  license:
    name: Apache-2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
  title: Plugin Access Manager — Identity API
  version: v1
servers: []
security: []
tags:
  - description: >-
      M2M-only: each plugin declares its own permissions, roles and M2M
      contract, and the server reconciles them.
    name: Declarations
  - description: >-
      A tenant administrator's customers. Each partner holds what its
      credentials may DO (permissions, per product) and WHERE they may do it
      (scope, per product and dimension); an M2M application attached to one is
      confined to that intersection. Administrator-only: authorized on the
      "partners" resource, and every route resolves the owning organization from
      the caller's token.
    name: Partners
paths:
  /v1/declarations/{slug}:
    put:
      tags:
        - Declarations
      summary: Upsert a plugin declaration
      description: >-
        Creates or replaces the access-manager declaration for the given plugin
        slug. Called by the product itself, at start-up, with its own M2M token:
        the token must be issued to the target application (app.ClientId ==
        token azp) and that application must declare for the slug, else `403`.


        The body has two sections and each is replaced ONLY when present, so a
        product can publish one without touching the other:

        - the ACCESS section — `permissions`, `roles` and `m2m` together (a
        permission names the roles it grants to, so they are applied as one full
        sync: what the section no longer declares is removed). Served in
        single-tenant deployments only; in multi-tenant it answers `501`,
        because the tenant manager materializes permissions there.

        - the SCOPE section — `scope.dimensions`, the dimensions the product's
        routes address instances by, and `partners`, the product's opt-in to
        partner credentials (either alone is the section), plus `levels` in the
        scope-only form. It replaces the product's scope catalog wholesale and
        is served in single- AND multi-tenant deployments: the catalog is global
        per product. Every partner write is validated against it, and `GET
        /v1/scope-catalog/{product}` reads it back.


        A body carrying both sections in multi-tenant applies the scope section
        and then answers `501` for the access section.


        Failures: `422` — the manifest is malformed (the message lists every
        violation), declares neither section, its `service` is not the slug, no
        application is registered for the calling client, or the identity
        provider refused the scope catalog (the message repeats its code, e.g.
        `CRDH-3120`); `403` — the token does not speak for the slug; `409` — the
        access section could not be persisted; `501` — the access section in a
        multi-tenant deployment; `503 IDE-0060` — the identity provider is
        unavailable, retry later; `500` — another dependency failed, retry.
      operationId: upsertDeclaration
      parameters:
        - description: Plugin slug that owns the declaration being upserted.
          in: path
          name: slug
          required: true
          schema:
            description: Plugin slug that owns the declaration being upserted.
            examples:
              - plugin-fees
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeclarationManifest'
              description: Declaration manifest to upsert for the slug.
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeclarationResponseBody'
          description: OK
        '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
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Service Unavailable
        default:
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Detail'
          description: Error
      security:
        - BearerAuth: []
components:
  schemas:
    DeclarationManifest:
      additionalProperties: false
      properties:
        levels:
          description: >-
            Scope-only form of the permissions' levels: one entry per permission
            that declares a level, sent when the permission section is not.
            Forwarded with the scope catalog. Not accepted next to permissions,
            which carry their level themselves; alone it is not a section, since
            it would replace the catalog's dimensions and opt-in with nothing.
          items:
            $ref: '#/components/schemas/DeclarationLevel'
          type:
            - array
            - 'null'
        m2m:
          $ref: '#/components/schemas/DeclarationM2M'
          description: >-
            Bilateral machine-to-machine contract: which targets this plugin
            calls and whether it is callable.
        partners:
          description: >-
            Opts the product in to partner credentials: a partner can be granted
            this product only when it is true. Forwarded with the scope catalog;
            a body carrying only this member is a scope-only body. Replaces the
            opt-in whenever the catalog is replaced.
          examples:
            - true
          type: boolean
        permissions:
          description: >-
            Permissions declared by the plugin; one entry per (resource,
            action).
          items:
            $ref: '#/components/schemas/DeclarationPermission'
          type:
            - array
            - 'null'
        roles:
          description: >-
            Roles declared by the plugin. Names may use '/' for hierarchy but
            must not repeat the service prefix (it is added automatically).
          items:
            $ref: '#/components/schemas/DeclarationRole'
          type:
            - array
            - 'null'
        scope:
          $ref: '#/components/schemas/DeclarationScope'
          description: >-
            Dimensions the product's routes address instances by — the WHERE a
            partner's scope lines name. Replaces the product's catalog when
            present; leaves it untouched when absent. Accepted in single- and
            multi-tenant deployments.
        service:
          description: >-
            Service that owns this declaration; matches the caller token (the
            plugin- prefix is kept).
          examples:
            - plugin-fees
          type: string
        version:
          description: >-
            Advisory manifest version. Must be a positive integer. Excluded from
            the canonical content hash.
          examples:
            - 3
          format: int64
          type: integer
      type: object
    DeclarationResponseBody:
      additionalProperties: false
      properties:
        slug:
          description: Slug of the declaration that was upserted.
          examples:
            - plugin-fees
          type: string
        status:
          description: Outcome of the upsert operation.
          examples:
            - accepted
          type: string
      required:
        - slug
        - status
      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
    DeclarationLevel:
      additionalProperties: false
      properties:
        action:
          description: Action of the permission.
          examples:
            - update
          type: string
        level:
          description: >-
            tenant, or the name of a dimension of this manifest's scope, spelled
            exactly.
          examples:
            - organizationId
          type: string
        resource:
          description: Resource the permission applies to, written bare.
          examples:
            - ledgers
          type: string
      type: object
    DeclarationM2M:
      additionalProperties: false
      properties:
        exposed:
          description: When true, this plugin is callable as an M2M target.
          examples:
            - true
          type: boolean
        needs:
          description: Target service slugs this plugin calls via M2M tokens.
          examples:
            - - midaz
          items:
            type: string
          type:
            - array
            - 'null'
      type: object
    DeclarationPermission:
      additionalProperties: false
      properties:
        action:
          description: Semantic action (create/read/update/delete or a domain verb).
          examples:
            - create
          type: string
        effect:
          description: >-
            Permission effect. Must be allow: a deny effect is recorded but
            never enforced, so it is refused.
          examples:
            - allow
          type: string
        level:
          description: >-
            How wide one instance of the resource is: tenant, or the name of a
            dimension of this manifest's scope, spelled exactly. Partners are
            not granted a write on a resource wider than their scope.
          examples:
            - organizationId
          type: string
        resource:
          description: >-
            Resource the permission applies to, written bare (the service prefix
            is composed centrally).
          examples:
            - billing-packages
          type: string
        roles:
          description: >-
            Bare role names (each matching a declared role name) this permission
            grants to.
          examples:
            - - editor
          items:
            type: string
          type:
            - array
            - 'null'
      type: object
    DeclarationRole:
      additionalProperties: false
      properties:
        granted_to:
          description: >-
            Existing groups whose members receive this role. Group names must
            not contain '/'.
          items:
            $ref: '#/components/schemas/DeclarationGrant'
          type:
            - array
            - 'null'
        name:
          description: >-
            Bare role name; '/' denotes hierarchy. Must not repeat the service
            prefix (it is added automatically).
          examples:
            - editor
          type: string
      type: object
    DeclarationScope:
      additionalProperties: false
      properties:
        dimensions:
          description: >-
            Dimensions in tree order: the first is the top of the funnel (e.g.
            the organization) and every later one narrows inside the previous
            (e.g. a ledger inside it). An empty list declares that the product
            addresses no instance.
          items:
            $ref: '#/components/schemas/DeclarationDimension'
          type:
            - array
            - 'null'
      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
    DeclarationGrant:
      additionalProperties: false
      properties:
        group:
          description: Existing group whose members receive the role.
          examples:
            - fees-admins
          type: string
      type: object
    DeclarationDimension:
      additionalProperties: false
      properties:
        collection:
          description: >-
            The resource this dimension's own siblings live in. A partner scoped
            on this dimension may not list or create in it.
          examples:
            - ledgers
          type: string
        covers:
          description: >-
            Further resources whose items each belong to ONE value of this
            dimension. A partner scoped on this dimension may not address them
            without naming its value — that would reach its siblings' items.
            Must not repeat an entry or name the dimension's own collection.
          examples:
            - - balances
              - operations
          items:
            type: string
          type:
            - array
            - 'null'
        from:
          description: >-
            Where the product reads the value from on its own route: a path
            parameter, a query key, a header, or an
            application/x-www-form-urlencoded field.
          enum:
            - path
            - query
            - header
            - form
          examples:
            - path
          type: string
        label:
          description: Human name used in refusal messages.
          examples:
            - ledger
          type: string
        multi:
          description: >-
            A partner scope line for this dimension may carry more than one
            value.
          examples:
            - true
          type: boolean
        name:
          description: >-
            Attribute key the product sends on an authorize call and the value
            of field on a partner scope line. Compared by exact equality.
          examples:
            - ledgerId
          type: string
        param:
          description: >-
            Name of what the value is read from: the path parameter (from path),
            the query key (from query), the header, compared case-insensitively
            (from header), or the form field (from form). Two dimensions may not
            read the same one.
          examples:
            - ledger_id
          type: string
        parent:
          description: >-
            Name of the dimension whose instances hold this one's (a ledgerId
            dimension's parent is organizationId), declaring the hierarchy the
            order of the list does not. Must name another declared dimension,
            and following parents from any dimension must end at a dimension
            without one. Absent for a top-level dimension.
          examples:
            - organizationId
          type: string
        required:
          description: Every partner granted this product must scope this dimension.
          examples:
            - false
          type: boolean
      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.