KnoxCall Signup API

Headless account creation — the only unauthenticated endpoint, designed so scripts and AI agents can onboard without a browser.

Operations 2

POST /signup Start account creation (headless signup) #
POST /signup/claim Collect starter credentials for a signup claim #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/knoxcall:knoxcall-signup-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

knoxcall-signup-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: KnoxCall Client Management Signup API
  version: '1.0'
  description: 'The KnoxCall Client Management API provides programmatic access to manage your API gateway

    resources including routes, secrets, clients, environments, webhooks, and API keys. ## Authentication


    All requests must include an OAuth 2.1 access token **or** a legacy API key. The

    recommended path for new integrations is the first-party SDKs, which mint, cache, and

    refresh OAuth tokens for you (start with `knoxcall login` on a developer machine, or

    `KNOXCALL_CLIENT_ID` / `KNOXCALL_CLIENT_SECRET` in CI). You can authenticate using any of:


    - **Authorization header (OAuth 2.1 bearer token — recommended)**: `Authorization: Bearer `

    - **Authorization header (legacy API key)**: `Authorization: Bearer tk_xxx_yyy`

    - **x-api-key header (legacy API key)**: `x-api-key: tk_xxx_yyy`


    OAuth 2.1 access tokens (`kc_` prefix) are minted at the root-host token endpoint

    `https://api.knoxcall.com/oauth/token` (not under `/v1`) using the `client_credentials`

    or `authorization_code` grant. See the `OAuth2` security scheme and the `/oauth/*`

    endpoints. Some resources that are managed exclusively through OAuth reject API-key auth

    with a `403 use_oauth` error. Long-lived API keys remain fully supported. They come in three types:

    - **Standard keys** (`tk_` prefix) are used on the production API (`api.knoxcall.com`). - **Test keys** (`tk_` prefix, type=test) are used on the sandbox API (`sandbox.knoxcall.com`). - **Enterprise Access Keys** (`AKE` prefix) follow the format `AKE[16chars]:[40chars]`. Using a test key on the production API (or vice versa) will return a `403 wrong_key_type` error. ## Request IDs


    Every response — success or error — includes an `X-Request-Id` response header (a UUID). Its value is identical to the `request_id` field in the response body (`meta.request_id`

    on success, `error.request_id` on error). Quote it when contacting support. ## API Versioning


    The API is versioned on two axes:


    - **Major version — the `/v1` path prefix.** Breaking changes ship under a new major

    (`/v2`). `/v1` never receives a breaking change. - **Minor version — the `KnoxCall-Version` header (dated, `YYYY-MM-DD`).** Send

    `KnoxCall-Version: 2026-08-05` to pin the behaviour of a specific dated release. Omit

    the header to get the newest minor version. Every response echoes the resolved version

    back in a `KnoxCall-Version` response header. Within a major version, **additive changes are not breaking** and may appear without a new

    dated version: new endpoints, new optional request fields, new response fields, new

    `error.type` values, and new enum members. Clients must tolerate unknown response fields

    and unknown `error.type` values (see the `ErrorResponse` schema). An **unknown or malformed** `KnoxCall-Version` value returns `400` with

    `error.type: invalid_api_version`. ## Rate Limiting


    API keys can have optional rate limits configured. When a limit is configured, the

    following headers are emitted on **every** response (not only `429`s):

    - `X-RateLimit-Limit` -- maximum requests allowed per window

    - `X-RateLimit-Remaining` -- remaining requests in the current window

    - `X-RateLimit-Reset` -- epoch seconds at which the window resets


    When the limit is exceeded the API returns `429` (`rate_limit_exceeded` / `rate_limited`)

    and additionally sets `Retry-After` (seconds until the window resets). ## DPoP sender-constrained tokens, and the server nonce


    A token minted with a `cnf.jkt` binding (DPoP, RFC 9449) requires a `DPoP` proof header

    on every request, signed over the request''s method and canonical public URL. **Every response to a request that carried a `DPoP` header also carries a

    `DPoP-Nonce` response header** -- mints, refusals and successful calls alike, on

    `POST /oauth/token`, `/v1`, the tenant data plane, the self-hosted proxy and the AI

    gateway. The value is stateless and changes every minute; the server accepts the current

    and the previous minute''s, so a nonce is good for 60-120 seconds.'
  contact:
    name: KnoxCall Support
    url: https://knoxcall.com
    email: support@knoxcall.com
  license:
    name: Proprietary
    url: https://knoxcall.com/terms
servers:
- url: https://api.knoxcall.com/v1
  description: Production
- url: https://sandbox.knoxcall.com/v1
  description: Sandbox (test keys only)
security:
- OAuth2: []
- BearerAuth: []
- ApiKeyAuth: []
tags:
- name: Signup
  description: Headless account creation — the only unauthenticated endpoint, designed so scripts and AI agents can onboard without a browser.
paths:
  /signup:
    post:
      operationId: createAccount
      summary: Start account creation (headless signup)
      description: 'Start creating a KnoxCall account and tenant - no browser required.


        This call **never returns a credential**. It always answers `202` with an opaque

        `claim_handle` and emails a sign-in link to the address. The tenant is created when that

        link is clicked; the caller then polls `POST /v1/signup/claim` with the handle to collect

        a one-time **test API key** and the seeded Test-mode demo route, so the whole integration

        can still be built headlessly against `sandbox.knoxcall.com`.


        Only available on `api.knoxcall.com` (not the sandbox host). Rate limited to 3 signups

        per hour per IP.


        **Account existence is NOT observable on this endpoint.** Every response - accepted or

        rejected - is independent of whether the address already has an account: same status,

        same body shape, a handle drawn from the same generator with the same lifetime, and no

        credential anywhere. An address that already has an account receives a sign-in link and a

        handle that stays `pending` forever. (Before 2026-08-28 this endpoint answered `201` plus

        an instant API key for a new address and `202` for a registered one, which was a complete

        account-existence oracle; that contract is withdrawn.)'
      tags:
      - Signup
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - email
              - tenant_name
              properties:
                email:
                  type: string
                  format: email
                  description: Account owner's email. Receives the sign-in link that creates the tenant.
                tenant_name:
                  type: string
                  maxLength: 100
                  description: Company or team name.
                full_name:
                  type: string
                  description: Owner's full name. Defaults to the tenant name.
                tenant_slug:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$
                  minLength: 3
                  maxLength: 40
                  description: 'Desired subdomain ({slug}.knoxcall.com): 3-40 chars of lowercase letters, digits and hyphens, starting and ending with a letter or digit. Reserved subdomains (www, api, admin, well-known brand names, ...) and the sandbox- prefix are rejected. Omit to have one derived from tenant_name automatically (recommended for headless callers - the server converges on an available slug on its own).'
                country:
                  type: string
                  minLength: 2
                  maxLength: 2
                  pattern: ^[A-Za-z]{2}$
                  description: 'ISO 3166-1 alpha-2 country code. Used to derive the data region when region is omitted. Enforced: anything else (an alpha-3 code such as USA, or a country name) is a 400 invalid_request, not a deferred failure - the column is two characters wide.'
                region:
                  type: string
                  enum:
                  - us
                  - eu
                  - au
                  description: Explicit data region. Immutable after signup.
            example:
              email: dev@example.com
              tenant_name: Acme Inc
      responses:
        '202':
          description: 'Accepted - the ONLY success status, returned identically whether or not the address

            already has an account. Poll `claim_path` with `claim_handle` once the emailed

            sign-in link has been clicked.

            '
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                        - pending
                      claim_handle:
                        type: string
                        description: 'Opaque single-use handle (`sck_` plus 43 url-safe characters). Present on every accepted signup. Treat it as a secret: it is what collects the starter credential.'
                      claim_path:
                        type: string
                        description: Path to poll, on the same API host.
                        example: /v1/signup/claim
                      poll_after_seconds:
                        type: integer
                        description: Suggested delay between polls.
                      expires_at:
                        type: string
                        format: date-time
                        description: After this, the handle is rejected exactly like an unknown one.
                      message:
                        type: string
                      documentation:
                        type: string
                  meta:
                    type: object
                    properties:
                      request_id:
                        type: string
        '400':
          $ref: '#/components/responses/ValidationError'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /signup/claim:
    post:
      operationId: claimSignup
      summary: Collect starter credentials for a signup claim
      description: 'Poll a `claim_handle` returned by `POST /v1/signup`.


        Answers `202` (`status: pending`) until the emailed sign-in link has been clicked, then

        `200` once with the tenant, the seeded Test-mode demo route and a one-time test API key.

        The credential is minted at collection time and shown exactly once - a second poll gets

        `409`.


        Unauthenticated by design: the claim handle is the bearer. It is unguessable (256 bits),

        single-use, and expires. An unknown handle and an expired one get the same `404`, and a

        handle whose link has not been clicked polls `pending` regardless of whether the address

        already had an account.'
      tags:
      - Signup
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - claim_handle
              properties:
                claim_handle:
                  type: string
                  description: The handle returned by POST /v1/signup.
            example:
              claim_handle: sck_Yy3n0Rz1qF8mKpX2sVb7dH9tLwQ4eJ6uA1cN5gZ8kT0
      responses:
        '200':
          description: Ready. The starter api_key value is returned exactly once - store it before anything else.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                        - ready
                      tenant:
                        type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                          slug:
                            type: string
                          name:
                            type: string
                          region:
                            type: string
                          plan:
                            type: string
                      starter:
                        type: object
                        description: Seeded Test-mode demo route + one-time test API key.
                        properties:
                          route:
                            type:
                            - object
                            - 'null'
                            properties:
                              id:
                                type: string
                                format: uuid
                              name:
                                type: string
                              target_base_url:
                                type: string
                          api_key:
                            type: object
                            properties:
                              id:
                                type: string
                                format: uuid
                              key_id:
                                type: string
                              api_key:
                                type: string
                                description: Plaintext test key - shown only in this response.
                              key_prefix:
                                type: string
                              key_type:
                                type: string
                                enum:
                                - test
                          sandbox_host:
                            type: string
                          curl:
                            type: string
                            description: Ready-to-run curl that exercises the demo route.
                      sandbox:
                        type: object
                        properties:
                          management_api:
                            type: string
                          proxy_host:
                            type: string
                          note:
                            type: string
                      documentation:
                        type: string
                  meta:
                    type: object
                    properties:
                      request_id:
                        type: string
        '202':
          description: 'Not ready - the sign-in link has not been used yet. Identical for a claim whose link

            is simply unclicked and for one raised against an address that already had an account.

            '
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                        - pending
                      message:
                        type: string
                      poll_after_seconds:
                        type: integer
                      expires_at:
                        type: string
                        format: date-time
                  meta:
                    type: object
                    properties:
                      request_id:
                        type: string
        '400':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  responses:
    NotFound:
      description: The requested resource was not found.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        KnoxCall-Version:
          $ref: '#/components/headers/KnoxCall-Version'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: not_found
              message: Route not found.
              request_id: 550e8400-e29b-41d4-a716-446655440000
    ValidationError:
      description: The request failed validation (always `400 validation_error`). A malformed idempotency key returns `400 invalid_idempotency_key`.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        KnoxCall-Version:
          $ref: '#/components/headers/KnoxCall-Version'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            validation_error:
              summary: Request body failed validation
              value:
                error:
                  type: validation_error
                  message: name and target_base_url are required.
                  request_id: 550e8400-e29b-41d4-a716-446655440000
            invalid_idempotency_key:
              summary: Malformed idempotency key
              value:
                error:
                  type: invalid_idempotency_key
                  message: X-Idempotency-Key must be a non-empty string of at most 255 characters.
                  request_id: 550e8400-e29b-41d4-a716-446655440000
    Conflict:
      description: A resource with the same unique identifier already exists, or the resource is in use.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        KnoxCall-Version:
          $ref: '#/components/headers/KnoxCall-Version'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: conflict
              message: A route with this name already exists.
              request_id: 550e8400-e29b-41d4-a716-446655440000
    RateLimited:
      description: 'Too many requests. Retry after the indicated time. The `X-RateLimit-*` headers are

        also present on non-429 responses whenever a limit is configured.

        '
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
        KnoxCall-Version:
          $ref: '#/components/headers/KnoxCall-Version'
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: rate_limit_exceeded
              message: API rate limit exceeded. Please retry later.
              request_id: 550e8400-e29b-41d4-a716-446655440000
  headers:
    X-RateLimit-Remaining:
      description: 'Remaining requests in the current window. Emitted on every response whenever a rate

        limit is configured for the authenticated key.

        '
      schema:
        type: integer
      example: 999
    KnoxCall-Version:
      description: 'The dated API version (`YYYY-MM-DD`) that served this response. Equals the value

        of the `KnoxCall-Version` request header when supplied, otherwise the newest version.

        '
      schema:
        type: string
        pattern: ^\d{4}-\d{2}-\d{2}$
      example: '2026-08-05'
    X-Request-Id:
      description: 'Unique identifier for this request (UUID). Present on **every** response and equal

        to the `request_id` field in the response body. Quote it when contacting support.

        '
      schema:
        type: string
        format: uuid
      example: 550e8400-e29b-41d4-a716-446655440000
    X-RateLimit-Reset:
      description: 'Epoch seconds at which the current rate-limit window resets. Emitted on every

        response whenever a rate limit is configured for the authenticated key.

        '
      schema:
        type: integer
      example: 1735689600
    Retry-After:
      description: Seconds to wait before retrying. Set on `429` responses.
      schema:
        type: integer
      example: 30
    X-RateLimit-Limit:
      description: 'Maximum requests allowed per window. Emitted on every response whenever a rate

        limit is configured for the authenticated key.

        '
      schema:
        type: integer
      example: 1000
  schemas:
    ErrorResponse:
      type: object
      description: Standard error response envelope.
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              description: 'Machine-readable error type. The set is OPEN — endpoints define specific types beyond the common ones below, so clients should branch on the types they know and fall back to the HTTP status class for anything unrecognized. Common values: authentication_required, invalid_api_key, wrong_key_type, subscription_inactive, rate_limit_exceeded, validation_error, forbidden, not_found, method_not_allowed (405, carries Allow), conflict, in_use, plan_limit, plan_feature, internal_error, dependency_unavailable (503, carries Retry-After), invalid_request, invalid_definition, not_cancellable, invalid_idempotency_key, idempotency_key_reuse, request_in_progress, system_client_protected, use_oauth, use_dpop_nonce (401 on /v1 and the data plane, 400 on POST /oauth/token; see "DPoP sender-constrained tokens").'
              example: validation_error
            message:
              type: string
              description: Human-readable error message.
              example: name and target_base_url are required.
            request_id:
              type: string
              format: uuid
              description: Unique identifier for this request.
              example: 550e8400-e29b-41d4-a716-446655440000
          required:
          - type
          - message
          - request_id
      required:
      - error
  securitySchemes:
    OAuth2:
      type: oauth2
      description: 'OAuth 2.1 authentication — recommended for new integrations. Access tokens

        (`kc_` prefix) are minted at the root-host token endpoint

        `https://api.knoxcall.com/oauth/token` and passed as `Authorization: Bearer <access_token>`.

        Public clients must use PKCE with the `authorization_code` grant; confidential

        clients may use `client_credentials`. The first-party SDKs and the

        `knoxcall login` CLI handle token minting, caching, refresh, and DPoP for you.

        '
      flows:
        clientCredentials:
          tokenUrl: https://api.knoxcall.com/oauth/token
          refreshUrl: https://api.knoxcall.com/oauth/token
          scopes:
            routes:read: Read routes.
            routes:write: Create, update, and delete routes.
            secrets:read: Read secret metadata. Does NOT include retrieving a live OAuth2 access token.
            secrets:oauth_token: Retrieve a live upstream OAuth2 access token (GET /v1/secrets/{id}/oauth2/token). Distinct from secrets:read because the call refreshes the credential at the provider.
            secrets:write: Create, update, and delete secrets.
            vaults:detokenize: Turn a vault token back into its original value. Required by GET /v1/vaults/{vault}/tokens/{token}, by a token template reference in a /v1/proxy body, and by creating a `detokenize` route action. Distinct from vaults:read, which lists vaults and token metadata and never returns a value.
            transit:decrypt: Open a portable `kc:` ciphertext with a tenant transit key. Required by creating a `decrypt` route action, which stores a standing instruction to open every matching field on every later request through that route. It is the same capability name the policy engine uses for POST /v1/decrypt. No routes scope satisfies it — managing a route is not permission to open the data flowing through it.
            proxy:invoke: Call the one-shot ephemeral proxy (any method on /v1/proxy). Required for EVERY verb — the proxy is one capability behind one handler, and a bodyless GET spends an escrowed credential exactly as a POST does. Neither proxy:read nor proxy:write satisfies it.
            clients:read: Read clients.
            clients:write: Manage clients.
            webhooks:write: Manage webhooks.
            workflows:write: Manage and execute workflows.
            '*:*': Full access (all scopes).
        authorizationCode:
          authorizationUrl: https://api.knoxcall.com/oauth/authorize
          tokenUrl: https://api.knoxcall.com/oauth/token
          refreshUrl: https://api.knoxcall.com/oauth/token
          scopes:
            routes:read: Read routes.
            routes:write: Create, update, and delete routes.
            secrets:read: Read secret metadata. Does NOT include retrieving a live OAuth2 access token.
            secrets:oauth_token: Retrieve a live upstream OAuth2 access token (GET /v1/secrets/{id}/oauth2/token). Distinct from secrets:read because the call refreshes the credential at the provider.
            secrets:write: Create, update, and delete secrets.
            vaults:detokenize: Turn a vault token back into its original value. Required by GET /v1/vaults/{vault}/tokens/{token}, by a token template reference in a /v1/proxy body, and by creating a `detokenize` route action. Distinct from vaults:read, which lists vaults and token metadata and never returns a value.
            transit:decrypt: Open a portable `kc:` ciphertext with a tenant transit key. Required by creating a `decrypt` route action, which stores a standing instruction to open every matching field on every later request through that route. It is the same capability name the policy engine uses for POST /v1/decrypt. No routes scope satisfies it — managing a route is not permission to open the data flowing through it.
            proxy:invoke: Call the one-shot ephemeral proxy (any method on /v1/proxy). Required for EVERY verb — the proxy is one capability behind one handler, and a bodyless GET spends an escrowed credential exactly as a POST does. Neither proxy:read nor proxy:write satisfies it.
            clients:read: Read clients.
            clients:write: Manage clients.
            webhooks:write: Manage webhooks.
            workflows:write: Manage and execute workflows.
            '*:*': Full access (all scopes).
    PhantomToken:
      type: http
      scheme: bearer
      description: "The AI **data plane** credential — a `kp_`-prefixed capability token, not a\nManagement API credential. It is minted per agent (or per MCP resource) at\n`POST /v1/ai-gateway/agents/{agentId}/tokens`, or exchanged from a CI workload's\nOIDC token at `POST /v1/oauth/token`, and it is the ONLY credential `/v1/ai/*`\naccepts: an OAuth `kc_` access token or a `tk_` API key is refused there, and a\n`kp_` token is refused on `api.knoxcall.com`.\n\nThree header spellings are accepted, so an existing provider SDK can be pointed at\nthe agent URL without rewriting its auth:\n\n    Authorization: Bearer kp_live_a_...\n    x-api-key: kp_live_a_...\n    x-knox-ai-key: kp_live_a_...\n\n`kp_live_` is refused on the sandbox host and `kp_test_` on the Live host — the env\nsegment is enforced, not cosmetic. DPoP-bound tokens additionally require a valid\n`DPoP` proof header on every request, signed over the canonical public URL rather\nthan a rewritten `Host`.\n\nA rejected credential answers `401`/`403` in the data-plane error envelope with the\ncause in `code` and in the `X-Auth-Failure` response header.\n"
    BearerAuth:
      type: http
      scheme: bearer
      description: 'Legacy long-lived API key authentication via the Authorization header.

        Pass your API key as: `Authorization: Bearer tk_xxx_yyy`.


        Standard keys (`tk_` prefix) are accepted on `api.knoxcall.com`.

        Test keys (type=test) are accepted on `sandbox.knoxcall.com`.

        Enterprise Access Keys (`AKE` prefix) are accepted on both.


        Fully supported, but prefer the `OAuth2` scheme (or the SDKs, which handle

        OAuth for you) for new integrations.

        '
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: 'Legacy API key authentication via the `x-api-key` header.

        This is an alternative to Bearer token authentication for clients that cannot

        set the `Authorization` header. Prefer the `OAuth2` scheme for new integrations.

        '
    CapabilityTokenAuth:
      type: http
      scheme: bearer
      description: 'Single-use client capability token (`kct_` prefix), passed as

        `Authorization: Bearer kct_...`. Minted server-side via

        `POST /client-tokens` and bound either to one exact payload + action

        (`decrypt`, `detokenize`) or to one vault + a closed set of page

        origins (`tokenize`). This is NOT an API key — it authenticates only

        the `POST /client/decrypt`, `POST /client/detokenize` and

        `POST /client/tokenize` consume endpoints and is invalidated after its

        first use.

        '