> ## 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 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`.
- 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; `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-0066` — the identity provider is unavailable, retry later; `500` — another dependency failed, retry.



## OpenAPI

````yaml /en/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 Access Manager identity component.
  license:
    name: Lerian Studio General License
  title: Plugin Access Manager — Identity API
  version: v1
servers:
  - url: https://identity.sandbox.lerian.net
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 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`.

        - 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; `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-0066` — 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:
        integrations:
          description: >-
            Lerian services this product calls while serving a request (e.g.
            midaz). A partner's request this product relays to one of them is
            also decided there by the partner's own rules for that service, so
            the Console offers to set those rules when the partner is granted
            this product. Forwarded with the scope catalog; alone it is a
            scope-only body. Entries must be non-empty, unique, different from
            service and contain no wildcard, compared trimmed and
            case-insensitively.
          examples:
            - - midaz
          items:
            type: string
          type:
            - array
            - 'null'
        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. 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'
        party_roles:
          description: >-
            Declared roles a party robot (a fund or an originator calling
            through the product) assumes; a product that declares none admits
            none. Requires exposed when non-empty.
          examples:
            - - operator
          items:
            type: string
          type:
            - array
            - 'null'
        roles:
          description: >-
            Declared roles an M2M caller of this service assumes. Requires
            exposed when non-empty. Absent: every declared role except
            m2m.service_roles is assumable. A group-less partner role never is.
            []: none.
          examples:
            - - editor
          items:
            type: string
          type:
            - array
            - 'null'
        service_roles:
          description: >-
            Declared roles an internal service caller assumes; never assumable
            by a client M2M credential. A role named service-{caller} is held by
            that caller's service credential alone; the others by every caller
            that has no such role. Requires exposed when non-empty. Each must
            bind no group, must not be in m2m.roles or m2m.party_roles, and must
            not be editor or editor-role.
          examples:
            - - lender-m2m-role
          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.
          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'
        explicit:
          description: >-
            A partner reaches this dimension's collection only through ids its
            scope names: a scoped partner that names none for it is refused on
            the collection, instead of reaching every item. For a collection the
            product looks up by the item's own id alone, so no ancestor
            dimension of the request confines it. Requires a collection and
            excludes required.
          examples:
            - true
          type: boolean
        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.