openapi: 3.1.0
info:
  title: PhoneAgent
  version: 0.1.0
  summary: Temporary exclusive phone numbers for AI agents.
  description: Pay via x402 (USDC on Base L2). Charged only when an SMS actually
    arrives; silent rentals settle 0 and incur no on-chain transaction. No
    signup, no API keys — discover and pay purely over HTTP 402.
  license:
    name: Proprietary
  contact:
    name: PhoneAgent
servers:
  - url: https://api.phoneagent.xyz
x-x402:
  scheme: exact
  network: base
  asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
  payTo: "0xF6705e3dA2d2f98aFd3944E4a6B0D6Ca931e576A"
paths:
  /countries:
    get:
      operationId: listCountries
      summary: List available countries and per-service pricing.
      description: "Returns the live storefront. Each country entry lists which
        services are offered, at which SKU ($1 activate, $2 activate_premium, or
        $5 activate_max), and the current inventory pool — higher pool = better
        deliverability. Upstream SMS deliverability is non-deterministic
        (typical ~30-50% per attempt); plan to retry — failed rentals expire
        with no charge. Paid discovery: 0.01 USDC per call on Base, settled
        inline. The dust price exists to register this resource in CDP Bazaar's
        discovery index — agents barely notice the cost."
      responses:
        "200":
          description: OK
          headers:
            X-Payment-Response:
              description: Base64-encoded `{tx, network}` of the on-chain settlement that paid
                for this call.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CountryList"
              examples:
                snapshot:
                  value:
                    - code: US
                      name: United States
                      services:
                        - code: go
                          name: Google / Gmail
                          mode: activate
                          price_usdc: 1
                          pool: 265876
                        - code: ig
                          name: Instagram
                          mode: activate
                          price_usdc: 1
                          pool: 214368
                        - code: tg
                          name: Telegram
                          mode: activate_premium
                          price_usdc: 2
                          pool: 40123
                          notes:
                            - Sign up only via the official Telegram mobile app
                              — web/desktop won't accept SMS codes during
                              registration.
                            - If the number is already registered, cancel and
                              rent a different one.
        "402":
          description: Payment required (or payment invalid). Body is the same x402
            PaymentRequirements shape as /rent, priced at 0.01 USDC.
          headers:
            X-Payment-Required:
              description: Base64-encoded PaymentRequirements (same as body).
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRequired"
        "502":
          description: Settlement failed at the facilitator.
  /rent:
    post:
      operationId: rentNumber
      summary: Rent one exclusive phone number.
      description: Returns 402 Payment Required with x402 `exact` PaymentRequirements.
        After the client signs an EIP-3009 transferWithAuthorization for the
        quoted price ($1 activate, $2 activate_premium, $5 activate_max) and
        re-POSTs with X-Payment, we verify the signature with the facilitator,
        reserve a number upstream, and return the rental. We only call /settle
        (capture funds) when an SMS actually arrives; otherwise we never call
        settle and the authorization expires unused — the client is never
        charged. Upstream SMS deliverability is non-deterministic (typical
        ~30-50% per attempt); plan to retry.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RentRequest"
            examples:
              basic:
                value:
                  country: US
                  service: go
                  duration_minutes: 10
              withWebhook:
                value:
                  country: ID
                  service: other
                  duration_minutes: 15
                  webhook_url: https://agent.example.com/sms-hook
      responses:
        "200":
          description: Number rented (after successful payment verification).
          headers:
            X-Payment-Response:
              description: Base64-encoded receipt. With scheme `exact` the server holds the
                signed authorization and only calls settle if an SMS arrives; no
                settlement tx exists yet when this response is returned.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rental"
              example:
                rental_id: r_01HW8Z2KQ7
                phone_number: "14155551234"
                token: tok_7fG3
                expires_at: 2026-04-24T18:35:00Z
                mode: activate
        "400":
          description: Invalid request or service×country not offered.
        "402":
          description: Payment required (or payment invalid).
          headers:
            X-Payment-Required:
              description: Base64-encoded PaymentRequirements (same as body).
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentRequired"
        "503":
          description: No inventory available for this combination.
  /sms/{rental_id}:
    get:
      operationId: getSms
      summary: Fetch received SMS (short or long poll).
      description: Pass `wait=1..25` to long-poll — the connection is held up to that
        many seconds, returning as soon as an SMS lands. `wait=0` or omitted
        returns current list immediately. `token` is returned at rent time.
      parameters:
        - name: rental_id
          in: path
          required: true
          schema:
            type: string
        - name: token
          in: query
          required: true
          schema:
            type: string
        - name: wait
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            maximum: 25
      responses:
        "200":
          description: Array of SMS messages (possibly empty).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SmsList"
              example:
                - from: VERIFY
                  text: Your code is 478392
                  receivedAt: 1714000100000
        "404":
          description: Rental not found or token invalid.
  /rentals/{rental_id}:
    get:
      operationId: getRentalStatus
      summary: Get rental status.
      parameters:
        - name: rental_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Rental status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RentalStatus"
              example:
                rental_id: r_01HW8Z2KQ7
                status: settled
                mode: activate
                service: go
                country: US
                phone_number: "14155551234"
                created_at: 2026-04-24T18:20:00Z
                expires_at: 2026-04-24T18:40:00Z
                sms_received: true
                settle_tx_hash: 0xebd4ec367a…
        "404":
          description: Rental not found.
components:
  schemas:
    ServiceCode:
      type: string
      enum:
        - tg
        - wa
        - go
        - fb
        - ig
        - tw
        - ds
        - mm
        - wx
        - pm
        - other
      description: "One of the supported services: `tg` — Telegram, `wa` — WhatsApp,
        `go` — Google / Gmail, `fb` — Facebook, `ig` — Instagram, `tw` — Twitter
        / X, `ds` — Discord, `mm` — Microsoft, `wx` — Apple / iCloud, `pm` —
        PayPal, `other` — Any other service."
    Mode:
      type: string
      enum:
        - activate
        - activate_premium
        - activate_max
    RentRequest:
      type: object
      required:
        - country
        - service
        - duration_minutes
      properties:
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code. Must be a code offered right now —
            see /countries.
          enum:
            - UA
            - KZ
            - CN
            - PH
            - MM
            - ID
            - MY
            - KE
            - TZ
            - VN
            - US
            - IL
            - HK
            - PL
            - GB
            - MG
            - NG
            - EG
            - IN
            - IE
            - KH
            - LA
            - RS
            - ZA
            - RO
            - CO
            - EE
            - CA
            - MA
            - GH
            - AR
            - UZ
            - DE
            - LT
            - HR
            - SE
            - NL
            - LV
            - AT
            - BY
            - TH
            - SA
            - MX
            - TW
            - ES
            - BD
            - CZ
            - LK
            - PE
            - PK
            - NZ
            - BR
            - KR
            - MN
            - NP
            - CY
            - FR
            - HU
            - BG
            - FI
            - IT
            - PY
            - AE
            - NO
            - PT
            - GE
            - MD
            - KW
            - AM
            - AU
            - CH
            - DK
            - SG
        service:
          $ref: "#/components/schemas/ServiceCode"
        duration_minutes:
          type: integer
          minimum: 1
          maximum: 20
          description: Minutes to wait for SMS. v1 serves single-SMS activations up to 20
            minutes.
        webhook_url:
          type: string
          format: uri
    Rental:
      type: object
      properties:
        rental_id:
          type: string
        phone_number:
          type: string
          description: E.164 number without the leading `+` (e.g. `442026022220` for +44
            20 2602 2220).
        token:
          type: string
          description: Bearer token for GET /sms/{rental_id}.
        expires_at:
          type: string
          format: date-time
        mode:
          $ref: "#/components/schemas/Mode"
    CountryList:
      type: array
      items:
        $ref: "#/components/schemas/CountryEntry"
    CountryEntry:
      type: object
      properties:
        code:
          type: string
          description: ISO 3166-1 alpha-2.
        name:
          type: string
        services:
          type: array
          items:
            type: object
            properties:
              code:
                $ref: "#/components/schemas/ServiceCode"
              name:
                type: string
              mode:
                $ref: "#/components/schemas/Mode"
              price_usdc:
                type: number
              pool:
                type: integer
              notes:
                type: array
                items:
                  type: string
                description: Optional platform-specific guidance — signup gotchas, recovery
                  rules, etc. Empty or absent if no special notes apply.
    Sms:
      type: object
      properties:
        from:
          type: string
        text:
          type: string
        receivedAt:
          type: integer
    SmsList:
      type: array
      items:
        $ref: "#/components/schemas/Sms"
    RentalStatus:
      type: object
      properties:
        rental_id:
          type: string
        status:
          type: string
          enum:
            - created
            - paid
            - active
            - settled
            - expired
            - released
        mode:
          $ref: "#/components/schemas/Mode"
        service:
          $ref: "#/components/schemas/ServiceCode"
        country:
          type: string
        phone_number:
          type: string
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        sms_received:
          type: boolean
        settle_tx_hash:
          type:
            - string
            - "null"
          description: USDC transfer tx hash on Base (set when SMS arrived and we called
            /settle). Null on silent rentals — the EIP-3009 authorization
            expires unused with no on-chain footprint.
    PaymentRequired:
      type: object
      properties:
        x402Version:
          type: integer
        accepts:
          type: array
          items:
            type: object
            properties:
              scheme:
                type: string
                example: exact
              network:
                type: string
                example: base
              maxAmountRequired:
                type: string
                example: "1000000"
                description: Exact amount in USDC base units (6 decimals). $1 activate, $2
                  activate_premium, $5 activate_max.
              resource:
                type: string
              description:
                type: string
              mimeType:
                type: string
              payTo:
                type: string
              maxTimeoutSeconds:
                type: integer
                description: EIP-3009 validBefore window (seconds). Covers the rental duration +
                  5 min buffer.
              asset:
                type: string
