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

# Log in

> Authenticate with email and password. On success the response sets the
`aropay_session` cookie (httpOnly, `SameSite=Lax`, 24 h TTL) and returns
the user.

If the account has TOTP enabled, no session is created yet; the
response instead carries `requiresTotp: true` and a short-lived
`pendingToken` to exchange at `POST /auth/login/totp`.

Limited to 10 attempts per 15 minutes per IP; 5 consecutive failures
lock the account for 15 minutes.




## OpenAPI

````yaml /api-reference/openapi.yaml post /auth/login
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:
  /auth/login:
    post:
      tags:
        - Auth
      summary: Log in
      description: |
        Authenticate with email and password. On success the response sets the
        `aropay_session` cookie (httpOnly, `SameSite=Lax`, 24 h TTL) and returns
        the user.

        If the account has TOTP enabled, no session is created yet; the
        response instead carries `requiresTotp: true` and a short-lived
        `pendingToken` to exchange at `POST /auth/login/totp`.

        Limited to 10 attempts per 15 minutes per IP; 5 consecutive failures
        lock the account for 15 minutes.
      operationId: login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - password
              properties:
                email:
                  type: string
                  format: email
                  description: Account email address.
                password:
                  type: string
                  description: Account password.
            example:
              email: you@company.com
              password: correct-horse-battery-staple
      responses:
        '200':
          description: Session created, or TOTP step required.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                properties:
                  ok:
                    const: true
                  data:
                    oneOf:
                      - type: object
                        title: Session created
                        required:
                          - user
                        properties:
                          user:
                            $ref: '#/components/schemas/User'
                      - type: object
                        title: TOTP required
                        required:
                          - requiresTotp
                          - pendingToken
                        properties:
                          requiresTotp:
                            const: true
                          pendingToken:
                            type: string
                            description: >-
                              Short-lived token to exchange at `POST
                              /auth/login/totp`.
              example:
                ok: true
                data:
                  user:
                    id: ckusr0001abcd
                    email: you@company.com
                    name: Ada Lovelace
                    company: Example Corp
                    mustChangePassword: false
                    totpEnabled: false
                    lastLoginAt: '2026-08-13T09:00:00.000Z'
                    createdAt: '2026-06-01T12:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          description: Invalid email or password (also returned while locked out).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                ok: false
                error:
                  code: invalid_credentials
                  message: Invalid email or password.
        '403':
          $ref: '#/components/responses/AccountDisabled'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
      security: []
components:
  schemas:
    User:
      type: object
      description: A sandbox account.
      required:
        - id
        - email
        - name
        - company
        - mustChangePassword
        - totpEnabled
        - lastLoginAt
        - createdAt
      properties:
        id:
          type: string
          examples:
            - ckusr0001abcd
        email:
          type: string
          format: email
        name:
          type:
            - string
            - 'null'
        company:
          type:
            - string
            - 'null'
        mustChangePassword:
          type: boolean
          description: True until the forced first-login password rotation is completed.
        totpEnabled:
          type: boolean
        lastLoginAt:
          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:
    BadRequest:
      description: Malformed body or failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: validation_error
              message: Request validation failed.
              details:
                - path: amount
                  message: Required
    AccountDisabled:
      description: The account has been deactivated.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            ok: false
            error:
              code: account_disabled
              message: This account is disabled.
    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.

````