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

# List tokens

> Lists the tokens in your tenant, one page at a time.

Requires the `emsp:tokens:read` scope.



## OpenAPI

````yaml /openapi/emsp-token/openapi.yaml get /emsp/v1/tokens
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/tokens:
    get:
      tags:
        - Tokens
      summary: List tokens
      description: |-
        Lists the tokens in your tenant, one page at a time.

        Requires the `emsp:tokens:read` scope.
      operationId: tokens_list
      parameters:
        - name: status
          required: false
          in: query
          description: >-
            Only tokens in one of these statuses. Repeat the parameter to pass
            several: `status=ACTIVE&status=SUSPENDED`
          schema:
            type: array
            items:
              type: string
              enum:
                - PROVISIONED
                - ACTIVE
                - SUSPENDED
                - EXPIRED
                - REVOKED
        - name: type
          required: false
          in: query
          description: >-
            Only tokens of one of these types. Repeat the parameter to pass
            several
          schema:
            type: array
            items:
              type: string
              enum:
                - RFID
                - APP_USER
                - AD_HOC_USER
                - EMAID
                - OTHER
        - name: search
          required: false
          in: query
          schema:
            type: string
        - name: after
          required: false
          in: query
          description: >-
            Opaque pagination cursor: the `pagination.end_cursor` of a previous
            response
          schema:
            type: string
        - name: limit
          required: false
          in: query
          description: Results per page
          schema:
            minimum: 1
            maximum: 200
            default: 50
            type: integer
        - name: sort_by
          required: false
          in: query
          description: Property to sort by
          schema:
            type: string
            enum:
              - created_at
              - last_updated
              - uid
        - name: sort_direction
          required: false
          in: query
          schema:
            default: asc
            type: string
            enum:
              - asc
              - desc
      responses:
        '200':
          description: One page of tokens
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedTokenDto'
        '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'
        '422':
          description: >-
            A query parameter 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:
    PaginatedTokenDto:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/TokenDto'
        pagination:
          $ref: '#/components/schemas/PaginationMetaDto'
      required:
        - data
        - pagination
    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
    TokenDto:
      type: object
      properties:
        schema_version:
          type: string
          enum:
            - v1
          description: Canonical schema version
        id:
          type: string
          maxLength: 64
          description: Stable internal token id, commonly a UUIDv7
        tenant_id:
          type: string
          description: Owning tenant, set by the platform
        type:
          type: string
          enum:
            - RFID
            - APP_USER
            - AD_HOC_USER
            - EMAID
            - OTHER
        uid:
          type: string
          maxLength: 50
          description: Primary credential identifier, unique within the tenant
        contract_id:
          type: string
          maxLength: 50
          description: Contract or credential identity, e.g. an eMAID
        issuer:
          type: string
          maxLength: 100
          description: Issuer name associated with the credential
        country_code:
          type: string
          nullable: true
          minLength: 2
          maxLength: 2
        party_id:
          type: string
          nullable: true
          minLength: 3
          maxLength: 3
        visual_number:
          type: string
          nullable: true
          maxLength: 64
        whitelist:
          type: string
          enum:
            - ALWAYS
            - ALLOWED
            - ALLOWED_OFFLINE
            - NEVER
          description: OCPI whitelist policy
        status:
          type: string
          enum:
            - PROVISIONED
            - ACTIVE
            - SUSPENDED
            - EXPIRED
            - REVOKED
        language:
          type: string
          nullable: true
          description: Preferred language of the token owner
        valid_from:
          type: string
          nullable: true
          format: date-time
        valid_until:
          type: string
          nullable: true
          format: date-time
        whitelist_distributed:
          type: boolean
          description: >-
            Whether the token has been distributed to whitelist-capable CPO
            networks
        group_id:
          type: string
          nullable: true
          maxLength: 50
          description: >-
            OCPI token group id used in roaming payloads. Unrelated to Spirii
            EMSP token groups
        emsp_group_id:
          type: string
          nullable: true
          maxLength: 50
          description: >-
            Primary Spirii EMSP token group, set at provision time. A
            convenience only: membership is many-to-many and is managed through
            POST /emsp/v1/token-groups/{groupId}/tokens
        metadata:
          type: object
          additionalProperties: true
          nullable: true
          description: >-
            Name/value pairs validated against the tenant's active TOKEN
            metadata schema
        last_authorization:
          type: string
          nullable: true
          format: date-time
        created_at:
          type: string
          format: date-time
          description: When the token was created in the platform registry
        last_updated:
          type: string
          format: date-time
          description: Canonical publication timestamp propagated to roaming partners
        invalidated_at:
          type: string
          nullable: true
          format: date-time
        audit:
          $ref: '#/components/schemas/AuditMetaDto'
      required:
        - schema_version
        - id
        - tenant_id
        - type
        - uid
        - contract_id
        - issuer
        - whitelist
        - status
        - whitelist_distributed
        - created_at
        - last_updated
        - audit
    PaginationMetaDto:
      type: object
      properties:
        end_cursor:
          type: string
          nullable: true
          description: >-
            Cursor for the end of the current page. Pass it back as `after` to
            fetch the next page; null on the last page.
        has_next:
          type: boolean
          description: Whether more results exist
      required:
        - end_cursor
        - has_next
    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
    AuditMetaDto:
      type: object
      properties:
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        created_by:
          type: string
          description: Actor who created the resource
      required:
        - created_at
        - updated_at
        - created_by
  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

````