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

# Create API key

> **Session-only.** Create a key. The response's `plaintext` (the full
`aro_sk_…` secret) is shown exactly once; only its SHA-256 hash is
stored. Accounts can hold at most 10 active keys.




## OpenAPI

````yaml /api-reference/openapi.yaml post /keys
openapi: 3.1.0
info:
  title: AroPay API
  version: 1.0.0
  description: |
    The AroPay API for orchestrating sealed, private payments on public
    ledgers, built on the Aro Confidential Rails (Zama Protocol FHE). The
    current deployment is an MVP sandbox for B2B users on Ethereum Sepolia,
    with Mainnet to follow.

    Every response (except the flat Sandbox probes)
    uses the shared envelope: `{ "ok": true, "data": … }` on success and
    `{ "ok": false, "error": { "code", "message", "details?" } }` on failure.

    Amounts are decimal strings in human units (e.g. `"125.50"`). Write
    requests must send `Content-Type: application/json`.
  contact:
    name: Aro Media
    url: https://aro.media
servers:
  - url: https://aropay.aro.media/api/v1
    description: Sandbox
  - url: http://aropay.localhost:3000/api/v1
    description: Local development
security:
  - apiKeyAuth: []
  - sessionCookie: []
tags:
  - name: Auth
    description: Password, TOTP, and passkey login; session inspection and logout.
  - name: Profile & security
    description: Profile, password, two-factor authentication, and passkey management.
    x-group: Profile & security
  - name: API keys
    description: Programmatic credentials. Creation and revocation are session-only.
  - name: Wallets
    description: Custodial and external (watch-only) wallets.
  - name: Balances
    description: Balance triples and lightweight gas reads.
  - name: Money movement
    description: Faucet funding, minting, transfers, and redemptions.
  - name: Transactions
    description: History, single-transaction reads, and notarized receipts.
  - name: Sandbox
    description: Unauthenticated status and echo probes with a flat response shape.
paths:
  /keys:
    post:
      tags:
        - API keys
      summary: Create API key
      description: |
        **Session-only.** Create a key. The response's `plaintext` (the full
        `aro_sk_…` secret) is shown exactly once; only its SHA-256 hash is
        stored. Accounts can hold at most 10 active keys.
      operationId: createApiKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Label identifying the consumer, e.g. "CI integration".
                expiresInDays:
                  type: integer
                  minimum: 1
                  maximum: 365
                  description: Optional expiry. Omit for a non-expiring key.
            example:
              name: CI integration
              expiresInDays: 90
      responses:
        '201':
          description: Key created. Store `plaintext` now; it is never shown again.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                properties:
                  ok:
                    const: true
                  data:
                    type: object
                    required:
                      - key
                      - plaintext
                    properties:
                      key:
                        $ref: '#/components/schemas/ApiKey'
                      plaintext:
                        type: string
                        description: The full secret. Shown only in this response.
              example:
                ok: true
                data:
                  key:
                    id: ckkey0001abcd
                    name: CI integration
                    keyPrefix: aro_sk_3f9
                    lastUsedAt: null
                    expiresAt: '2026-11-11T09:00:00.000Z'
                    revokedAt: null
                    createdAt: '2026-08-13T09:00:00.000Z'
                  plaintext: aro_sk_3f9d…
        '400':
          description: Validation failure or key limit reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                ok: false
                error:
                  code: key_limit_reached
                  message: You already have 10 active API keys.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/SessionRequired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - sessionCookie: []
components:
  schemas:
    ApiKey:
      type: object
      description: >-
        API key metadata. The secret itself is only ever returned once, at
        creation.
      required:
        - id
        - name
        - keyPrefix
        - lastUsedAt
        - expiresAt
        - revokedAt
        - createdAt
      properties:
        id:
          type: string
        name:
          type: string
        keyPrefix:
          type: string
          description: First characters of the key, for identification.
          examples:
            - aro_sk_3f9
        lastUsedAt:
          type:
            - string
            - 'null'
          format: date-time
        expiresAt:
          type:
            - string
            - 'null'
          format: date-time
        revokedAt:
          type:
            - string
            - 'null'
          format: date-time
        createdAt:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      description: The shared failure envelope.
      required:
        - ok
        - error
      properties:
        ok:
          const: false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code; match on this, not `message`.
            message:
              type: string
            details:
              description: Structured context, e.g. per-field validation issues.
  responses:
    Unauthorized:
      description: Missing or invalid credential.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: invalid_api_key
              message: Invalid API key.
    SessionRequired:
      description: Session-only endpoint called with an API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: session_required
              message: This action requires a browser session.
    RateLimited:
      description: Rate limit exceeded.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: rate_limited
              message: Too many requests. Try again shortly.
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: internal_error
              message: An unexpected error occurred.
  securitySchemes:
    apiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: aro_sk_…
      description: |
        AroPay API key, sent as `Authorization: Bearer aro_sk_…`. Created in
        the dashboard under Settings → API keys. Takes precedence over a
        session cookie when both are present.
      x-default: aro_sk_your_key_here
    sessionCookie:
      type: apiKey
      in: cookie
      name: aropay_session
      description: |
        Browser session cookie minted by `POST /auth/login` (or passkey
        login). httpOnly, `SameSite=Lax`, 24 h TTL. Endpoints marked
        **Session-only** accept only this credential and return
        `403 session_required` for API keys.

````