AhaSend API Keys API

Manage API keys for authentication and access control

Operations 5

Each operation below carries the questions people ask an LLM about it and the instructions they give an agent to run it. Generated by API Evangelist overlay

GET /v2/accounts/{account_id}/api-keys List the account's API keys · Get API Keys #
Ask an LLM
“Which API keys exist on my AhaSend account?”
“Can I page through all the API keys we've created?”
Tell an agent
List every API key on account {account_id}.
Show the first {limit} API keys for account {account_id}.
POST /v2/accounts/{account_id}/api-keys Create a new API key · Create API Key #
Ask an LLM
“How do I generate a new API key with only the scopes it needs?”
“Can I restrict a new API key so it only works from certain IP addresses?”
Tell an agent
Create an API key labeled {label} on account {account_id} with scopes {scopes}.
Generate a key called {label} with scopes {scopes} that only works from {ip_allow_list} on account {account_id}.
GET /v2/accounts/{account_id}/api-keys/{key_id} Look up one API key · Get API Key #
Ask an LLM
“What scopes does a specific API key of mine have?”
“Can I check the label and IP allow list on one particular key?”
Tell an agent
Show API key {key_id} on account {account_id}.
Get the scopes granted to key {key_id} in account {account_id}.
PUT /v2/accounts/{account_id}/api-keys/{key_id} Change an existing API key's label or scopes · Update API Key #
Ask an LLM
“How do I add or remove scopes on a key I already created?”
“Can I replace the IP allow list on an existing API key?”
Tell an agent
Rename API key {key_id} on account {account_id} to {label}.
Change the scopes of existing key {key_id} to {scopes} on account {account_id}.
DELETE /v2/accounts/{account_id}/api-keys/{key_id} Delete an API key · Delete API Key #
Ask an LLM
“How do I revoke an API key that may have leaked?”
“What happens when I permanently delete one of my account's API keys?”
Tell an agent destructive · confirm first
Delete API key {key_id} from account {account_id}.
Revoke key {key_id} on account {account_id} so it can no longer be used.

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/ahasend:ahasend-api-keys-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

ahasend-api-keys-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: AhaSend API v2 API Keys API
  description: The AhaSend API v2 allows you to send transactional emails, manage domains, webhooks, routes, API keys, and view statistics.
  version: 2.0.0
  contact:
    email: support@ahasend.com
  license:
    name: MIT
    identifier: MIT
servers:
- url: https://api.ahasend.com
  description: Production server
security:
- BearerAuth: []
tags:
- name: API Keys
  description: Manage API keys for authentication and access control
paths:
  /v2/accounts/{account_id}/api-keys:
    get:
      summary: Get API Keys
      description: Returns a list of API keys for the account
      operationId: getAPIKeys
      tags:
      - API Keys
      parameters:
      - name: account_id
        in: path
        required: true
        description: Account ID
        schema:
          type: string
          format: uuid
      - name: limit
        in: query
        description: Maximum number of items to return
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 100
      - name: after
        in: query
        description: Pagination cursor for the next page. Provide the value provided in `next_cursor` from the response.
        schema:
          type: string
      - name: before
        in: query
        description: Pagination cursor for the previous page.
        schema:
          type: string
      security:
      - BearerAuth:
        - api-keys:read
      x-code-samples:
      - lang: go
        label: AhaSend Go SDK
        source: "package main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  \"github.com/AhaSend/ahasend-go/api\"\n  \"github.com/google/uuid\"\n)\n\nfunc main() {\n  // Create API client with authentication\n  client := api.NewAPIClient(\n    api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n  )\n\n  accountID := uuid.New()\n\n  // Create context for the API call\n  ctx := context.Background()\n\n  // Call the ping endpoint\n  response, httpResp, err := client.APIKeysAPI.GetAPIKeys(\n    ctx,\n    accountID,\n    nil,\n    nil,\n  )\n  if err != nil {\n    log.Fatalf(\"Error getting API keys: %v\", err)\n  }\n\n  // Check response\n  if httpResp.StatusCode == 200 {\n    fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n    if response != nil {\n      fmt.Printf(\"Found %d API keys\\n\", len(response.Data))\n    }\n  } else {\n    fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n  }\n}\n"
      - lang: javascript
        label: Node.js 22+ (AhaSend SDK)
        source: 'import { AhaSendClient } from "@ahasend/sdk";


          const client = AhaSendClient.fromEnv();

          const page = await client.apiKeys.list({ limit: 20 });

          console.log("API keys listed.", { count: page.data.length });

          '
      responses:
        '200':
          description: List of API keys
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedAPIKeysResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      summary: Create API Key
      description: Creates a new API key with the specified scopes
      operationId: createAPIKey
      tags:
      - API Keys
      parameters:
      - name: account_id
        in: path
        required: true
        description: Account ID
        schema:
          type: string
          format: uuid
      - $ref: '#/components/parameters/IdempotencyKey'
      security:
      - BearerAuth:
        - api-keys:write
      x-code-samples:
      - lang: go
        label: AhaSend Go SDK
        source: "package main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  \"github.com/AhaSend/ahasend-go/api\"\n  \"github.com/AhaSend/ahasend-go/models/requests\"\n  \"github.com/google/uuid\"\n)\n\nfunc main() {\n  // Create API client with authentication\n  client := api.NewAPIClient(\n    api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n  )\n\n  accountID := uuid.New()\n\n  // Create context for the API call\n  ctx := context.Background()\n\n  // Create a new API key\n  response, httpResp, err := client.APIKeysAPI.CreateAPIKey(\n    ctx,\n    accountID,\n    requests.CreateAPIKeyRequest{\n      Label: \"My API Key\",\n      Scopes: []string{\n        \"messages:read:all\",\n        \"domains:read\",\n      },\n      // Optional: restrict this key to specific source IPs (CIDR\n      // blocks or bare IPv4/IPv6 addresses). Omit or leave empty to\n      // allow the key to be used from any IP.\n      IPAllowList: []string{\"203.0.113.0/24\", \"198.51.100.7\"},\n    },\n  )\n  if err != nil {\n    log.Fatalf(\"Error creating API key: %v\", err)\n  }\n\n  // Check response\n  if httpResp.StatusCode == 201 {\n    fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n    // SecretKey is the one-time secret, returned ONLY on create.\n    if response != nil && response.SecretKey != nil {\n      // Store this value immediately — it cannot be retrieved again later.\n      fmt.Printf(\"Created API key, secret key: %s\\n\", *response.SecretKey)\n    }\n  } else {\n    fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n  }\n}\n"
      - lang: javascript
        label: Node.js 22+ (AhaSend SDK)
        source: "import { AhaSendClient } from \"@ahasend/sdk\";\n\nconst client = AhaSendClient.fromEnv();\nconst apiKey = await client.apiKeys.create(\n  { label: \"Production API key\", scopes: [\"messages:send:all\"] },\n  { idempotencyKey: \"sdk-sample-create-api-key\" },\n);\nconsole.log(\"API key created.\", { id: apiKey.id, label: apiKey.label });\n"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAPIKeyRequest'
      responses:
        '201':
          description: API key created successfully
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/APIKey'
                - type: object
                  required:
                  - secret_key
                  properties:
                    secret_key:
                      type: string
                      readOnly: true
                      description: 'One-time secret key. Store it immediately. Exact

                        successful idempotent replays return the same secret

                        during the 5-minute encrypted replay window.

                        '
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '422':
          $ref: '#/components/responses/IdempotencyPayloadMismatch'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v2/accounts/{account_id}/api-keys/{key_id}:
    get:
      summary: Get API Key
      description: Returns a specific API key by ID
      operationId: getAPIKey
      tags:
      - API Keys
      parameters:
      - name: account_id
        in: path
        required: true
        description: Account ID
        schema:
          type: string
          format: uuid
      - name: key_id
        in: path
        required: true
        description: API Key ID
        schema:
          type: string
          format: uuid
      security:
      - BearerAuth:
        - api-keys:read
      x-code-samples:
      - lang: go
        label: AhaSend Go SDK
        source: "package main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  \"github.com/AhaSend/ahasend-go/api\"\n  \"github.com/google/uuid\"\n)\n\nfunc main() {\n  // Create API client with authentication\n  client := api.NewAPIClient(\n    api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n  )\n\n  accountID := uuid.New()\n\n  // Create context for the API call\n  ctx := context.Background()\n\n  // Call the ping endpoint\n  response, httpResp, err := client.APIKeysAPI.GetAPIKey(\n    ctx,\n    accountID,\n    uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n  )\n  if err != nil {\n    log.Fatalf(\"Error getting API key: %v\", err)\n  }\n\n  // Check response\n  if httpResp.StatusCode == 200 {\n    fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n    if response != nil {\n      fmt.Printf(\"API key: %#v\\n\", response)\n    }\n  } else {\n    fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n  }\n}\n"
      - lang: javascript
        label: Node.js 22+ (AhaSend SDK)
        source: 'import { AhaSendClient } from "@ahasend/sdk";


          const client = AhaSendClient.fromEnv();

          const keyId = "00000000-0000-4000-8000-000000000001";

          const apiKey = await client.apiKeys.get(keyId);

          console.log("API key found.", { id: apiKey.id, label: apiKey.label });

          '
      responses:
        '200':
          description: API key details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIKey'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: API key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      summary: Update API Key
      description: Updates an existing API key's label and scopes
      operationId: updateAPIKey
      tags:
      - API Keys
      parameters:
      - name: account_id
        in: path
        required: true
        description: Account ID
        schema:
          type: string
          format: uuid
      - name: key_id
        in: path
        required: true
        description: API Key ID
        schema:
          type: string
          format: uuid
      security:
      - BearerAuth:
        - api-keys:write
      x-code-samples:
      - lang: go
        label: AhaSend Go SDK
        source: "package main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  \"github.com/AhaSend/ahasend-go\"\n  \"github.com/AhaSend/ahasend-go/api\"\n  \"github.com/AhaSend/ahasend-go/models/requests\"\n  \"github.com/google/uuid\"\n)\n\nfunc main() {\n  // Create API client with authentication\n  client := api.NewAPIClient(\n    api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n  )\n\n  accountID := uuid.New()\n\n  // Create context for the API call\n  ctx := context.Background()\n\n  // Update an existing API key\n  response, httpResp, err := client.APIKeysAPI.UpdateAPIKey(\n    ctx,\n    accountID,\n    uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n    requests.UpdateAPIKeyRequest{\n      Label: ahasend.String(\"My API Key\"),\n      Scopes: &[]string{\n        \"messages:read:all\",\n        \"domains:read\",\n      },\n      // Replace the allowed source IPs. Use &[]string{} to clear the\n      // list (usable from any IP), or omit the field to leave the\n      // current list unchanged.\n      IPAllowList: &[]string{\"203.0.113.0/24\"},\n    },\n  )\n  if err != nil {\n    log.Fatalf(\"Error updating API key: %v\", err)\n  }\n\n  // Check response\n  if httpResp.StatusCode == 200 {\n    fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n    if response != nil {\n      fmt.Printf(\"Updated API key: %#v\\n\", response)\n    }\n  } else {\n    fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n  }\n}\n"
      - lang: javascript
        label: Node.js 22+ (AhaSend SDK)
        source: 'import { AhaSendClient } from "@ahasend/sdk";


          const client = AhaSendClient.fromEnv();

          const keyId = "00000000-0000-4000-8000-000000000001";

          const apiKey = await client.apiKeys.update(keyId, { label: "Renamed API key" });

          console.log("API key updated.", { id: apiKey.id, label: apiKey.label });

          '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAPIKeyRequest'
      responses:
        '200':
          description: API key updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIKey'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: API key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Conflict — the request would lock the calling API key out of its own account. Returned only when a key updates itself with an `ip_allow_list` that does not cover the caller's current source IP. No change is persisted. This is a terminal message-only conflict; it has no server error code or idempotency headers.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      summary: Delete API Key
      description: Deletes an API key
      operationId: deleteAPIKey
      tags:
      - API Keys
      parameters:
      - name: account_id
        in: path
        required: true
        description: Account ID
        schema:
          type: string
          format: uuid
      - name: key_id
        in: path
        required: true
        description: API Key ID
        schema:
          type: string
          format: uuid
      security:
      - BearerAuth:
        - api-keys:delete
      x-code-samples:
      - lang: go
        label: AhaSend Go SDK
        source: "package main\n\nimport (\n  \"context\"\n  \"fmt\"\n  \"log\"\n\n  \"github.com/AhaSend/ahasend-go/api\"\n  \"github.com/google/uuid\"\n)\n\nfunc main() {\n  // Create API client with authentication\n  client := api.NewAPIClient(\n    api.WithAPIKey(\"aha-sk-your-64-character-key\"),\n  )\n\n  accountID := uuid.New()\n\n  // Create context for the API call\n  ctx := context.Background()\n\n  // Call the ping endpoint\n  response, httpResp, err := client.APIKeysAPI.DeleteAPIKey(\n    ctx,\n    accountID,\n    uuid.MustParse(\"c5a32c40-b351-439f-8230-779daed3e42c\"),\n  )\n  if err != nil {\n    log.Fatalf(\"Error deleting API key: %v\", err)\n  }\n\n  // Check response\n  if httpResp.StatusCode == 200 {\n    fmt.Printf(\"✅ Status: %d\\n\", httpResp.StatusCode)\n    if response != nil {\n      fmt.Printf(\"Deleted API key: %#v\\n\", response)\n    }\n  } else {\n    fmt.Printf(\"❌ Unexpected status code: %d\\n\", httpResp.StatusCode)\n  }\n}\n"
      - lang: javascript
        label: Node.js 22+ (AhaSend SDK)
        source: 'import { AhaSendClient } from "@ahasend/sdk";


          const client = AhaSendClient.fromEnv();

          const keyId = "00000000-0000-4000-8000-000000000001";

          const result = await client.apiKeys.delete(keyId);

          console.log("API key deleted.", { message: result.message });

          '
      responses:
        '200':
          description: API key deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
              example:
                message: api key {id} ({label}) deleted successfully
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: API key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    APIKeyScope:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the scope
        created_at:
          type: string
          format: date-time
          description: When the scope was created
        updated_at:
          type: string
          format: date-time
          description: When the scope was last updated
        api_key_id:
          type: string
          format: uuid
          description: ID of the API key this scope belongs to
        scope:
          type: string
          description: The scope string
        domain_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Domain ID for domain-specific scopes; always present and null for non-domain scopes
      required:
      - id
      - created_at
      - updated_at
      - api_key_id
      - scope
      - domain_id
    APIKey:
      type: object
      properties:
        object:
          type: string
          enum:
          - api_key
          description: Object type identifier
        id:
          type: string
          format: uuid
          description: Unique identifier for the API key
        created_at:
          type: string
          format: date-time
          description: When the API key was created
        updated_at:
          type: string
          format: date-time
          description: When the API key was last updated
        last_used_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the API key was last used (updates every 5-10 minutes)
        account_id:
          type: string
          format: uuid
          description: Account ID this API key belongs to
        label:
          type: string
          description: Human-readable label for the API key
        public_key:
          type: string
          description: Public portion of the API key
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/APIKeyScope'
          description: Scopes granted to this API key
        ip_allow_list:
          type: array
          items:
            type: string
          description: Source IPs allowed to authenticate with this API key, as canonical CIDR blocks (a bare address is stored as a `/32` for IPv4 or `/128` for IPv6). Always present; an empty array means the key may be used from any source IP. When non-empty, an authenticated request whose client IP is not covered by an entry is rejected with HTTP 403 on every v2 endpoint, regardless of the key's scopes.
      required:
      - object
      - id
      - created_at
      - updated_at
      - last_used_at
      - account_id
      - label
      - public_key
      - scopes
      - ip_allow_list
    CreateAPIKeyRequest:
      type: object
      required:
      - label
      - scopes
      properties:
        label:
          type: string
          minLength: 1
          maxLength: 255
          description: Human-readable label for the API key; must not be empty
        scopes:
          type: array
          items:
            type: string
          minItems: 1
          description: Array of scope strings to grant to this API key
        ip_allow_list:
          type: array
          items:
            type: string
          description: Optional list of source IPs allowed to authenticate with this key. Each entry is a CIDR block (e.g. `203.0.113.0/24`) or a bare IPv4/IPv6 address (stored as a `/32` or `/128`). Entries are canonicalized (host bits are masked) and de-duplicated. The allow-all prefixes `0.0.0.0/0` and `::/0` are rejected, and at most 100 entries are allowed after de-duplication. Omit the field or pass an empty array to leave the key usable from any IP.
      example:
        label: Production API Key
        scopes:
        - messages:send:all
        - domains:read
        ip_allow_list:
        - 203.0.113.0/24
        - 198.51.100.7
    ErrorResponse:
      type: object
      description: 'Human-readable API error. The server currently sends no stable machine

        error code. In particular, clients must not parse `message` to

        distinguish IP-allow-list, scope, ownership, plan, self-lockout,

        suppression-duplicate, or other errors that share an HTTP status.

        '
      additionalProperties: false
      required:
      - message
      properties:
        message:
          type: string
          description: Error description
      example:
        message: Error message
    PaginatedAPIKeysResponse:
      type: object
      required:
      - object
      - data
      - pagination
      properties:
        object:
          type: string
          enum:
          - list
          description: Object type identifier
        data:
          type: array
          items:
            $ref: '#/components/schemas/APIKey'
          description: Array of API keys
        pagination:
          $ref: '#/components/schemas/PaginationInfo'
    SuccessResponse:
      type: object
      required:
      - message
      properties:
        message:
          type: string
          description: Success message
      example:
        message: Operation completed successfully
    UpdateAPIKeyRequest:
      type: object
      description: At least one non-null field must be provided, and at least one provided value must differ from the current value. Omitted or null fields are left unchanged.
      anyOf:
      - required:
        - label
        properties:
          label:
            type: string
      - required:
        - scopes
        properties:
          scopes:
            type: array
      - required:
        - ip_allow_list
        properties:
          ip_allow_list:
            type: array
      properties:
        label:
          type:
          - string
          - 'null'
          minLength: 1
          maxLength: 255
          description: Human-readable label for the API key. Omit to keep the existing label.
        scopes:
          type:
          - array
          - 'null'
          items:
            type: string
          minItems: 1
          description: Array of scope strings to grant to this API key
        ip_allow_list:
          type:
          - array
          - 'null'
          items:
            type: string
          description: Replacement list of allowed source IPs (CIDR blocks or bare IPv4/IPv6 addresses; canonicalized and de-duplicated, at most 100 entries, allow-all prefixes `0.0.0.0/0` and `::/0` rejected). Omit the field or send null to leave the existing list unchanged; send an empty array to clear it (key usable from any IP); send a non-empty array to replace it.
      example:
        label: Updated API Key
        scopes:
        - messages:send:{example.com}
        - domains:read
        ip_allow_list:
        - 203.0.113.0/24
    PaginationInfo:
      type: object
      required:
      - has_more
      properties:
        has_more:
          type: boolean
          description: Whether more items exist after the last item of this page
        next_cursor:
          type: string
          description: Pass as `after` to fetch the next page
        previous_cursor:
          type: string
          description: Pass as `before` to fetch the previous page; absent on a `before` page when the start of the list has been reached
      example:
        has_more: true
        next_cursor: eyJpZCI6MTIzNH0=
  responses:
    IdempotencyPayloadMismatch:
      description: 'Idempotency key was already used with a different HTTP method, resolved

        request path, or request body. This response includes neither

        `Idempotent-Replayed` nor `Retry-After`.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: idempotency key was already used with a different request payload
    IdempotencyConflict:
      description: 'Request in progress. This response always includes

        `Idempotent-Replayed: false` and a positive integer `Retry-After`.

        Clients must use that header tuple, not the human-readable message, to

        distinguish this retryable idempotency state from other HTTP 409s.

        '
      headers:
        Idempotent-Replayed:
          $ref: '#/components/headers/IdempotencyInProgress'
        Retry-After:
          $ref: '#/components/headers/IdempotencyRetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            message: A request with this idempotency key is already in progress
  headers:
    IdempotentReplayed:
      description: 'Indicates that this response is a stored replay of a previous identical request.

        When this header is absent, the response is not a stored replay. Ordinary fresh

        responses do not send `false`; that value is reserved for an in-progress 409.

        '
      schema:
        type: string
        enum:
        - 'true'
    IdempotencyInProgress:
      description: 'Identifies an idempotency-key execution that is still in progress. This

        header is emitted only on the specialized HTTP 409 response.

        '
      schema:
        type: string
        enum:
        - 'false'
    IdempotencyRetryAfter:
      description: 'Positive whole number of seconds after which the in-progress execution

        lease may be retried with the same key and unchanged request.

        '
      schema:
        type: integer
        minimum: 1
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: 'Optional idempotency key for safe request retries. Must be a unique string for each logical request.

        An identical request with a completed stored outcome returns the original status and body. An in-progress

        execution returns 409, a changed method/path/body returns 422, and a released 5xx execution may run again.

        Keys for non-secret responses expire after 24 hours. API-key create responses include a one-time `secret_key`,

        so successful encrypted replay responses for those operations expire after 5 minutes.

        '
      schema:
        type: string
        maxLength: 255
      example: user-12345-create-domain-20240101
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: aha-sk-64-CHARACTER-RANDOM-STRING
      description: 'API key for authentication. Non-empty Security Requirement values are

        AhaSend API-key roles. Roles listed within one requirement object are

        jointly required; separate requirement objects are alternatives.

        '