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

# Verify the calling key

> The **whoami** endpoint - returns the calling key's own object: scopes, status, and effective rate limits. Never any secret material (`masked_key` only).

**Scope: none required.** Self-introspection needs no extra scope - the caller is asking about the very key it authenticated with, so requiring a scope to answer "what am I" would be circular. Works with ANY `active` key, including one with an empty `scopes` array.

Use it to:
- **Prove a key works** before wiring it into a system (CI health checks, onboarding flows)
- **Debug a 403** - see exactly which scopes the key holds vs what the endpoint needs
- **Let integrations self-configure** - an agent or app discovers its own permissions and limits at runtime instead of hardcoding them

A key can describe itself but can never enumerate other keys, mint a new key, or revoke any key - all key management (mint, rotate, revoke, edit scopes) is **dashboard-only** at Settings → Developers → API Keys. This is deliberate: there is no `api_keys:manage` scope, so a compromised key cannot mint its own replacement or discover sibling keys.



## OpenAPI

````yaml /openapi.json get /v1/api_keys/me
openapi: 3.1.0
info:
  title: VEXiON cards Public API
  description: >-
    ## Rate limiting


    Two independent layers, checked in this order:


    1. **Per-IP throttle (pre-authentication).** Applied to every `/v1` request
    BEFORE the API key is checked, keyed on the caller's IP address:
       - **600 requests/minute** per IP - a flood ceiling, `429 rate_limit_error` / `rate_limited`.
       - **20 failed authentications per 15 minutes** per IP - a key-guessing ceiling. Only responses that are `401` (missing/invalid/revoked key) count against this ceiling; a `403` from a valid key that simply lacks a scope does not. `429 rate_limit_error` / `too_many_failed_attempts`.

       Both ceilings send `Retry-After` (seconds).
    2. **Per-key, per-bucket limiter (post-authentication).** Documented per
    endpoint (see each operation's `429` response and the `rate_limit` field on
    the API Key resource) - e.g. 60/min default, 120/min for transactions, plus
    the notification-broadcast and lookup-filter buckets described on their own
    endpoints.


    ### Rate-limit response headers


    Every rate-limited response carries the legacy `X-RateLimit-Limit` /
    `X-RateLimit-Remaining` / `X-RateLimit-Reset` family (the last is an **epoch
    timestamp**) AND the IETF standards-track headers, side by side:


    - `RateLimit-Policy: "<bucket>";q=<quota>;w=<window seconds>` - the static
    policy for the bucket that was hit.

    - `RateLimit: "<bucket>";r=<remaining>;t=<seconds until reset>` - live
    state. **`t` is a DURATION (seconds remaining), not the epoch timestamp
    `X-RateLimit-Reset` carries - the two are never the same value.**
  version: 1.0.0
servers:
  - url: https://api.vexioncards.one
    description: Production
  - url: https://migration.api.vexioncards.one
    description: Sandbox
security:
  - apiKey: []
tags:
  - name: Customers
  - name: Cards
  - name: Transactions
  - name: Notifications
  - name: Locations
  - name: Products
  - name: API keys
  - name: Organization
  - name: Webhook endpoints
  - name: Events
  - name: Custom fields
  - name: Team members
  - name: Icons
paths:
  /v1/api_keys/me:
    get:
      tags:
        - API keys
      summary: Verify the calling key
      description: >-
        The **whoami** endpoint - returns the calling key's own object: scopes,
        status, and effective rate limits. Never any secret material
        (`masked_key` only).


        **Scope: none required.** Self-introspection needs no extra scope - the
        caller is asking about the very key it authenticated with, so requiring
        a scope to answer "what am I" would be circular. Works with ANY `active`
        key, including one with an empty `scopes` array.


        Use it to:

        - **Prove a key works** before wiring it into a system (CI health
        checks, onboarding flows)

        - **Debug a 403** - see exactly which scopes the key holds vs what the
        endpoint needs

        - **Let integrations self-configure** - an agent or app discovers its
        own permissions and limits at runtime instead of hardcoding them


        A key can describe itself but can never enumerate other keys, mint a new
        key, or revoke any key - all key management (mint, rotate, revoke, edit
        scopes) is **dashboard-only** at Settings → Developers → API Keys. This
        is deliberate: there is no `api_keys:manage` scope, so a compromised key
        cannot mint its own replacement or discover sibling keys.
      operationId: getCurrentApiKey
      parameters: []
      responses:
        '200':
          description: Calling key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
              examples:
                Calling key:
                  summary: Bare api_key object
                  value:
                    id: key_8h2K9bP4mR7qX1nF3vT6wY5s
                    object: api_key
                    masked_key: vxn_sk_8h2K............................wY5sA0jL2dG8
                    label: Demo Coffee POS bridge
                    scopes:
                      - customers:read
                      - customers:write
                      - transactions:write
                    inactivity_auto_revoke_enabled: true
                    status: active
                    created_at: 1745832000
                    last_used_at: 1750691200
                    rate_limit:
                      default: 60/min
                      transactions: 120/min
                      notifications: 1/min, 50/day
          headers: {}
        '400':
          description: Malformed body or missing required field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers: {}
        '401':
          description: Missing, invalid, or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers: {}
        '429':
          description: Per-minute or per-day rate limit exceeded. Respect Retry-After.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers: {}
        '500':
          description: Server error. Retry-safe on idempotent calls.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers: {}
      deprecated: false
components:
  schemas:
    ApiKey:
      type: object
      description: >-
        Self-introspection view of the CALLING API key. A key can describe
        itself but can never enumerate other keys, mint a new key, or revoke any
        key - minting/rotation/revocation are dashboard-only (Settings →
        Developers → API Keys). This is the safety rail: a compromised key
        cannot mint a replacement before being revoked.
      required:
        - id
        - object
        - masked_key
        - label
        - scopes
        - status
        - created_at
      properties:
        id:
          type: string
          pattern: ^key_[A-Za-z0-9]+$
          description: >-
            Key identifier (`key_` prefix). Derived from the key material but
            NOT usable for authentication.
          example: key_8h2K9bP4mR7qX1nF3vT6wY5s
        object:
          type: string
          const: api_key
          description: Always `"api_key"`.
        masked_key:
          type: string
          description: >-
            Prefix-visible-suffix masked form: first 16 chars visible (vendor
            prefix + first 9 of suffix), last 12 visible, middle replaced with a
            dotted run. The plaintext secret is shown exactly once at mint time
            and never again - lost secret means rotate.
          example: vxn_sk_8h2K............................wY5sA0jL2dG8
        label:
          type: string
          minLength: 1
          maxLength: 60
          description: >-
            Human-readable label set at mint time (e.g. which system uses this
            key).
          example: Demo Coffee POS bridge
        scopes:
          type: array
          items:
            type: string
            enum:
              - customers:read
              - customers:write
              - cards:read
              - cards:write
              - products:read
              - products:write
              - custom_fields:read
              - custom_fields:write
              - transactions:read
              - transactions:write
              - notifications:send
              - notifications:read
              - webhooks:manage
              - webhooks:read
              - locations:read
              - locations:write
              - team_members:read
              - team_members:write
              - organization:read
          description: >-
            Permissions granted to this key, selected at creation time in the
            dashboard. The v1 scope registry is CLOSED at exactly these NINETEEN
            scopes; new scopes only ship as part of a versioned API change (see
            03-AUTHENTICATION.md). Read does not imply write. Products, custom
            fields, notification reads and webhook reads each have their own
            least-privilege scopes. `products:read`/`products:write` and
            `custom_fields:read`/`custom_fields:write` replace the earlier
            `cards:read`/`cards:write` borrowing. `notifications:read` covers
            the three GET routes (list/retrieve/deliveries);
            `notifications:send` still gates the broadcast POST and cancel
            DELETE. `webhooks:read` covers read-only webhook surfaces (endpoint
            list/retrieve/deliveries/event_types, and GET /v1/events);
            `webhooks:manage` still gates every mutation, including secret
            rotation. Self-introspection (GET /v1/api_keys/me) and the shared
            icon catalog require no scope at all. `organization:read` was added
            2026-07-27 because the invoices list returns real billing data. Keys
            created earlier were granted the new scopes they need, so no working
            integration broke.
          example:
            - customers:read
            - customers:write
            - transactions:write
        inactivity_auto_revoke_enabled:
          type: boolean
          description: >-
            When true, the key is auto-revoked after 90 days without a request.
            Default ON.
          example: true
        status:
          type: string
          enum:
            - active
            - paused
            - revoked
          description: >-
            Key lifecycle state. Only `active` keys authenticate. Paused/revoked
            transitions happen in the dashboard.
          example: active
        created_at:
          type: integer
          description: Unix seconds at mint time.
          example: 1745832000
        last_used_at:
          type:
            - integer
            - 'null'
          description: >-
            Unix seconds of the most recent authenticated request. Null if never
            used. Feeds the 90-day inactivity auto-revoke.
          example: 1750691200
        rate_limit:
          type: object
          description: >-
            The effective rate limits for this key, by bucket. Informational -
            the enforced values.
          properties:
            default:
              type: string
              description: Standard endpoints.
              example: 60/min
            transactions:
              type: string
              description: POST /v1/transactions hot path.
              example: 120/min
            notifications:
              type: string
              description: Broadcast accepts.
              example: 1/min, 50/day
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - rate_limit_error
                - idempotency_error
                - api_error
            code:
              type: string
            message:
              type: string
            retry_after:
              type: integer
              description: >-
                Seconds to wait before retrying. Present on rate-limit (429) and
                idempotency-key-in-use (409) errors; mirrors the `Retry-After`
                HTTP header. Absent on error types that carry no retry guidance.
              example: 5
            param:
              type:
                - string
                - 'null'
            request_id:
              type: string
            doc_url:
              type: string
              format: uri
            field_errors:
              type: array
              description: >-
                Per-field errors on a 422. At most 100 entries: a request with
                more errors gets the first 100.
              items:
                type: object
                properties:
                  param:
                    type: string
                  code:
                    type: string
                  message:
                    type: string
                  expected_currency:
                    type: string
                    description: >-
                      On `currency_mismatch`: the business currency the request
                      must use. Absent on every other field error.
          description: >-
            The error payload: type (category), code (machine-readable), message
            (human-readable), retry_after (seconds, on
            rate-limit/idempotency-in-use errors only), param (offending field),
            request_id, doc_url, field_errors[].
      description: >-
        Standard error envelope. Branch on `error.code`; include
        `error.request_id` in support requests. See the Errors guide.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        Your secret API key as a Bearer token: `Authorization: Bearer
        vxn_sk_...`. Keys are created and scoped in the dashboard - see
        [Authentication](/api-reference/introduction#authentication).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.