> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spirii.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Assign network to group

> Assigns a network to the group, so member tokens can charge on the CPOs it contains. Every network a token reaches, across all of its groups, must be built on the same BASE network.

Requires the `emsp:tokens:write` scope.



## OpenAPI

````yaml /openapi/emsp-token/openapi.yaml post /emsp/v1/token-groups/{groupId}/networks
openapi: 3.0.0
info:
  title: Spirii EMMS - Token Management API
  description: >-
    Register, provision, activate, and manage charging tokens and token groups.


    ## Authentication

    Every operation needs an OAuth2 client-credentials access token sent as
    `Authorization: Bearer <token>`. The API gateway rejects a missing,
    malformed, invalid or expired token with 401 before the request reaches the
    service. Each operation states the scope it requires: `emsp:tokens:read` for
    reads and `emsp:tokens:write` for changes, and `emsp:tokens:write` also
    allows every read. A token without the required scope gets 403.


    ## Errors

    Errors from the service are returned as RFC 9457 problem details with
    content type `application/problem+json`. `error_code` is a machine-readable
    code for the error, `retryable` says whether the same request may succeed
    later, and `correlation_id` matches the `X-Request-Id` response header.
    Validation failures return 422 with one entry per failed rule in `errors`,
    so a field can appear more than once; a 400 carries `errors` in the same
    shape. `errors[].field` is a JSON-path style location of the input:
    `body.type`, `body.tokens[3].uid`, `query.limit`, `path.tokenId`,
    `header.Idempotency-Key`, or `body` when the body is not valid JSON. Errors
    raised by the API gateway before a request reaches the service, such as a
    rejected access token, throttling or an unknown path, use a plain JSON body
    with a `message` field instead.


    `error_code` names the cause, and a status can carry more than one:


    | `error_code` | Status | Retryable | When |

    | --- | --- | --- | --- |

    | `INVALID_REQUEST_BODY` | 400 | no | The request body is not valid JSON |

    | `INVALID_PATH_ID` | 400 | no | A path id is not a UUID |

    | `INVALID_IDEMPOTENCY_KEY` | 400 | no | The Idempotency-Key header is
    missing or not a UUID |

    | `AUTH_CONTEXT_INVALID` | 401 | no | The `x-spirii-*` principal headers are
    missing or not UUIDs. Not raised behind the API gateway, which rejects a
    missing or invalid access token first with a plain JSON body |

    | `INSUFFICIENT_SCOPE` | 403 | no | The access token does not carry the
    scope this operation requires |

    | `RESOURCE_NOT_FOUND` | 404 | no | The requested resource was not found |

    | `UID_ALREADY_REGISTERED` | 409 | no | The uid is already registered in
    this tenant |

    | `IDEMPOTENCY_KEY_REUSED` | 409 | no | The Idempotency-Key was already used
    with a different request body |

    | `REQUEST_IN_PROGRESS` | 409 | yes | A request with this Idempotency-Key is
    still in progress, or failed less than a minute ago. Retry with the same key
    and body |

    | `RESOURCE_CONFLICT` | 409 | no | The resource is in a conflicting state.
    Not raised by the service today |

    | `PAYLOAD_TOO_LARGE` | 413 | no | The request body exceeds the 1 MB limit |

    | `UNSUPPORTED_MEDIA_TYPE` | 415 | no | The request body uses a charset or
    Content-Encoding the service does not accept |

    | `VALIDATION_FAILED` | 422 | no | The request body or a query parameter
    failed validation, and errors lists each failed rule |

    | `RATE_LIMIT_EXCEEDED` | 429 | yes | Too many requests. Not raised by the
    service today: the API gateway throttles first, with a plain JSON body |

    | `INTERNAL_SERVER_ERROR` | 500 | yes | An unexpected error occurred. Retry
    after retry_after_seconds |


    ## Idempotency

    Operations that take an `Idempotency-Key` header require a client-generated
    UUID. Repeating a request with the same key and body within 24 hours replays
    the original response and sets `Idempotent-Replay: true`; the same key with
    a different body returns 409. Keys are scoped to the method and path, so the
    same key on a different token or group is a separate request. If a request
    fails, its key stays reserved for about a minute: retrying with the same key
    and body in that window returns 409, and after it the same key runs the
    request again. Send a corrected request with a new key.


    ## Pagination

    List operations are cursor-paginated. Pass `pagination.end_cursor` from one
    response as `after` on the next, and stop when `pagination.has_next` is
    false.


    ## Asynchronous operations

    Bulk provisioning and token group lifecycle actions are accepted with 202
    and processed in the background. An endpoint for reading an operation by
    `operation_id` is not available yet.


    ## Resources

    On tokens and token groups, optional fields without a value are sent as
    `null` rather than left out. A token history event's `reason` and a group
    operation's `failures` are left out when they do not apply.


    ## Current limitations

    The service currently answers from fixtures: responses are generated per
    request and nothing is persisted, so reading a resource back does not return
    what was written. Every request resolves to the same single tenant, whatever
    credentials it carries. The 404 and 409 responses documented on each
    operation are the intended contract, but the service does not produce them
    yet: an unknown id returns a resource, and a lifecycle action succeeds from
    any status. The 409 for a `uid` that is already provisioned and for a reused
    `Idempotency-Key` are the exceptions and are returned today. Bulk import
    accepts the documented 1000 tokens in one request, and a request body over 1
    MB returns 413.
  version: v1
  contact: {}
servers:
  - url: https://api.spirii.com
    description: Production
security:
  - OAuth2ClientCredentials: []
tags:
  - name: Tokens
    description: Provision tokens, change their lifecycle and read their history
  - name: Token Groups
    description: >-
      Create token groups, manage their member tokens and network assignments,
      and change the lifecycle of every member at once
paths:
  /emsp/v1/token-groups/{groupId}/networks:
    post:
      tags:
        - Token Groups
      summary: Assign network to group
      description: >-
        Assigns a network to the group, so member tokens can charge on the CPOs
        it contains. Every network a token reaches, across all of its groups,
        must be built on the same BASE network.


        Requires the `emsp:tokens:write` scope.
      operationId: token_groups_assign_network
      parameters:
        - name: groupId
          required: true
          in: path
          description: The token group `group_id`
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          description: >-
            Client-generated unique key per operation intent (UUID). Same key +
            identical body replays the original response; same key + different
            body returns 409.
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignNetworkRequestDto'
      responses:
        '201':
          description: The new network assignment
          headers:
            Idempotent-Replay:
              description: >-
                Present and `true` when this response is a replay of an earlier
                request with the same Idempotency-Key
              schema:
                type: string
                enum:
                  - 'true'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenGroupNetworkAssignmentDto'
        '400':
          description: >-
            A path id is not a UUID (`INVALID_PATH_ID`). The Idempotency-Key
            header is missing or not a UUID (`INVALID_IDEMPOTENCY_KEY`), or the
            body is not valid JSON (`INVALID_REQUEST_BODY`)
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
        '401':
          description: >-
            The access token is missing, invalid or expired, rejected by the API
            gateway with a plain JSON body
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '403':
          description: >-
            The access token does not carry the scope this operation requires
            (`INSUFFICIENT_SCOPE`)
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
        '404':
          description: No token group with this id exists in your tenant
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
        '409':
          description: >-
            Assigning this network would give a member token networks on
            different BASE networks. The Idempotency-Key was already used with a
            different body (`IDEMPOTENCY_KEY_REUSED`), or that request is still
            in progress (`REQUEST_IN_PROGRESS`)
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
        '422':
          description: >-
            The request body failed validation (`VALIDATION_FAILED`). `errors`
            lists each failed rule
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
        '429':
          description: >-
            Too many requests, rejected by the API gateway with a plain JSON
            body
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '500':
          description: >-
            Unexpected server error. When `retryable` is true, retry after
            `retry_after_seconds`. Where the operation takes an Idempotency-Key,
            reuse the same key; a retry within about a minute may return 409
            while it is still reserved
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailDto'
components:
  schemas:
    AssignNetworkRequestDto:
      type: object
      properties:
        network_id:
          type: string
          format: uuid
          description: The network to assign
      required:
        - network_id
    TokenGroupNetworkAssignmentDto:
      type: object
      properties:
        group_id:
          type: string
          format: uuid
        network_id:
          type: string
          format: uuid
        network_name:
          type: string
          description: Display name of the network
        parent_network_id:
          type: string
          format: uuid
          description: The BASE network this network is built on
        emsp_identity_id:
          type: string
          nullable: true
          minLength: 1
          maxLength: 64
          description: The EMSP identity the network belongs to
        assigned_at:
          type: string
          format: date-time
          description: When the network was assigned
        assigned_by:
          type: string
          description: Who assigned the network
      required:
        - group_id
        - network_id
        - network_name
        - parent_network_id
        - assigned_at
        - assigned_by
    ProblemDetailDto:
      type: object
      properties:
        type:
          type: string
          example: /errors/validation-failed
          description: Error type URI
        title:
          type: string
          example: Validation Failed
        status:
          type: integer
          example: 422
        detail:
          type: string
        error_code:
          example: RESOURCE_NOT_FOUND
          description: >-
            Machine-readable error cause. The Errors section of the API
            description says when each is returned
          allOf:
            - $ref: '#/components/schemas/ErrorCode'
        correlation_id:
          type: string
          format: uuid
          description: Matches X-Request-Id header
        retryable:
          type: boolean
        retry_after_seconds:
          type: integer
          minimum: 1
        errors:
          type: array
          items:
            $ref: '#/components/schemas/FieldErrorDto'
      required:
        - type
        - title
        - status
        - detail
        - error_code
        - correlation_id
        - retryable
    ErrorCode:
      type: string
      enum:
        - INVALID_REQUEST_BODY
        - INVALID_PATH_ID
        - INVALID_IDEMPOTENCY_KEY
        - AUTH_CONTEXT_INVALID
        - INSUFFICIENT_SCOPE
        - RESOURCE_NOT_FOUND
        - UID_ALREADY_REGISTERED
        - IDEMPOTENCY_KEY_REUSED
        - REQUEST_IN_PROGRESS
        - RESOURCE_CONFLICT
        - PAYLOAD_TOO_LARGE
        - UNSUPPORTED_MEDIA_TYPE
        - VALIDATION_FAILED
        - RATE_LIMIT_EXCEEDED
        - INTERNAL_SERVER_ERROR
      description: >-
        Machine-readable error cause. The Errors section of the API description
        says when each is returned
    FieldErrorDto:
      type: object
      properties:
        field:
          type: string
          description: >-
            The input that failed, as a JSON-path style location: `body.type`,
            `body.tokens[3].uid`, `query.limit`, `path.tokenId`,
            `header.Idempotency-Key`, or `body` when the body is not valid JSON
          example: body.tokens[3].uid
        code:
          type: string
          enum:
            - INVALID_ENUM
            - INVALID_FORMAT
            - MISSING_REQUIRED
            - CONSTRAINT_VIOLATION
          description: Machine-readable validation error code
        message:
          type: string
          description: Human-readable error description
      required:
        - field
        - code
        - message
  securitySchemes:
    OAuth2ClientCredentials:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: >-
            https://auth.spirii.dk/realms/public-api/protocol/openid-connect/token
          scopes:
            emsp:tokens:read: >-
              Read tokens, token history, token groups and their network
              assignments
            emsp:tokens:write: >-
              Provision, update and change the lifecycle of tokens; create and
              manage token groups. Also grants everything `emsp:tokens:read`
              does

````