Anjal Domains API

Bring your own sending domain. Returns DNS records, verification state, and a BIND zone file export.

Operations 6

POST /v1/domains Register a custom domain #
GET /v1/domains List domains #
GET /v1/domains/{domain} Get a domain #
DELETE /v1/domains/{domain} Delete a domain #
GET /v1/domains/{domain}/verify Verify a domain #
GET /v1/domains/{domain}/zone-file Export BIND zone file #

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/anjal-app:anjal-app-domains-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

anjal-app-domains-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Anjal Domains API
  version: 1.0.0
  description: 'Anjal is a multi-tenant email API for AI agents.


    Create inboxes on the default `@send.anjal.app` domain or your own custom

    domains, send and receive mail, manage drafts, subscribe to real-time

    events over WebSocket, and isolate resources across workspaces so your

    reseller customers can serve their own end-customers under a single

    billing relationship.


    Install the TypeScript SDK: `npm install anjal`

    (npm)


    MCP server for Claude Desktop / Cursor: `npx -y anjal-mcp`

    (npm)


    ## Authentication


    Every request is authenticated with the `X-API-Key` header (or

    `Authorization: Bearer `). Four key prefixes, each with increasing

    scope:


    | Prefix | Scope | Use it for |

    |----------|-----------------------------------------|-----------------------------------------|

    | `sa_...` | Super admin | Tenant lifecycle (platform operator) |

    | `tk_...` | Tenant master | Everything in your tenant across workspaces |

    | `wk_...` | Workspace master | One workspace only |

    | `ak_...` | Inbox (agent) scoped | One inbox — its messages, drafts, lists |


    ## Resource hierarchy


    ```

    Tenant (billing root)

    └── Workspace (isolation container)

    ├── Inboxes

    │ ├── Messages / Threads / Attachments

    │ ├── Drafts

    │ └── Lists

    └── Domains

    ```


    ## Sending mail


    Mail is sent via `POST /v1/inboxes/{id}/send`. Replies go through

    `/reply/{messageId}`, reply-all through `/reply-all/{messageId}`, and

    forwards through `/forward/{messageId}`. The `From` header always uses

    the inbox''s **primary alias** — add a custom-domain address with

    `POST /v1/inboxes/{id}/addresses` to change it.


    ### Safe retries (Idempotency-Key)


    Every send endpoint (`/send`, `/reply`, `/reply-all`, `/forward` and

    `POST /v1/inboxes/{id}/drafts/{did}/send`) accepts an `Idempotency-Key`

    header. Send a unique value (a UUID works) and retry freely: the first

    request sends and stores its response; a retry with the same key and the

    identical request returns that stored response with the header

    `Idempotent-Replayed: true` and sends nothing.


    | Situation | Result |

    |---|---|

    | Same key, different request (body, inbox or endpoint) | `409 IDEMPOTENCY_KEY_REUSED` |

    | Same key while the first request is still running | `409 IDEMPOTENCY_IN_PROGRESS` |

    | Header present but blank, or longer than 255 characters | `400 IDEMPOTENCY_KEY_INVALID` |

    | The first send failed (non-2xx) | The key is freed; retry with the same key |


    Keys are scoped to your account and expire after 24 hours.


    ### Labels, open tracking, scheduled send


    - **Labels**: pass `labels` (array, or a comma-separated string) on any

    send, reply, reply-all, forward or draft. Change them later with

    `PATCH /v1/inboxes/{id}/messages/{messageId}` or

    `PATCH /v1/inboxes/{id}/threads/{threadId}`, and filter lists with

    `?labels=a,b`. Labels are stored lowercase and deduplicated; at most 32

    per message, each at most 64 characters, no commas.

    - **Open tracking**: `trackOpens: true` on a send with an HTML body adds a

    1x1 pixel (the stored copy never contains it). The first open fires

    `message.opened`. Opens are a hint, not proof: Apple Mail Privacy

    Protection fetches images on delivery, and clients that block images

    never report an open.

    - **Scheduled send**: set `sendAt` on a draft and Anjal sends it at that

    time. See the Drafts endpoints.

    - **Delivery status**: sent messages carry `deliveryStatus`

    (`delivered`, `bounced`, `complained` or `rejected`; null until known)

    and `deliveredAt`.


    ## Receiving mail


    Inbound mail arrives via SMTP on the default domain or any verified

    custom domain. Subscribe to delivery events either by registering a

    webhook (`POST /v1/webhooks`) or by opening a WebSocket

    (`wss://anjal.app/v1/ws`) — useful for in-line 2FA flows where

    the agent doesn''t have a public webhook receiver.'
servers:
- url: https://anjal.app
  description: Production
security:
- apiKeyHeader: []
tags:
- name: Domains
  description: Bring your own sending domain. Returns DNS records, verification state, and a BIND zone file export.
paths:
  /v1/domains:
    post:
      tags:
      - Domains
      summary: Register a custom domain
      description: 'Returns the DNS records you need to set at your registrar. Domain starts in `pending` status until you call the verify endpoint.


        **Domains that already get mail elsewhere (Google Workspace, Microsoft 365).**

        If the domain has MX records at another provider and you pass neither

        `sharedWith` nor `replaceMailProvider`, the call returns

        `422 DOMAIN_HAS_MAIL_PROVIDER` with `detectedProvider`, `mx` and a

        `fix` string, because pointing the MX at Anjal would cut off that mail.

        You then have three options:


        1. **Keep the provider** (`sharedWith: "google" | "microsoft" | "other"`).

        No MX record is returned: the provider stays the main mail server.

        The SPF TXT record is merged into the domain''s existing SPF (its

        `note` says to replace the existing record, since a domain may have

        only one). Anjal''s DMARC record is only included if the domain has

        none. The response includes `routingSteps` for the provider''s admin

        console: for Google, a Default routing rule that sends mail for

        non-recognized addresses to the Anjal host; for Microsoft, setting

        the Accepted domain to Internal relay plus an outbound connector to

        the Anjal smart host. In this setup the provider forwards mail for

        addresses it doesn''t know, so it keeps its own users and Anjal

        receives mail only for addresses the provider doesn''t have.

        2. **Use a subdomain** such as `agents.example.com`, which has no MX

        yet and can point at Anjal without touching the main domain''s mail.

        3. **Move all mail to Anjal** (`replaceMailProvider: true`). The MX

        record returned replaces the current provider''s.'
      operationId: createDomain
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - domain
              properties:
                domain:
                  type: string
                  example: mail.example.com
                workspaceId:
                  type: string
                  format: uuid
                  description: (Tenant master only) target workspace.
                sharedWith:
                  type: string
                  enum:
                  - google
                  - microsoft
                  - other
                  description: Keep this provider as the domain's main mail server and add Anjal mailboxes alongside it (`shared_with` also works).
                replaceMailProvider:
                  type: boolean
                  description: Move all mail for the domain to Anjal (the returned MX record replaces the current one).
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Domain'
                - type: object
                  properties:
                    message:
                      type: string
        '422':
          description: The domain already receives mail at another provider
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
                    example: DOMAIN_HAS_MAIL_PROVIDER
                  detectedProvider:
                    type: string
                    enum:
                    - google
                    - microsoft
                    - other
                  mx:
                    type: array
                    items:
                      type: string
                    example:
                    - aspmx.l.google.com
                  fix:
                    type: string
    get:
      tags:
      - Domains
      summary: List domains
      operationId: listDomains
      parameters:
      - name: workspace
        in: query
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  domains:
                    type: array
                    items:
                      $ref: '#/components/schemas/Domain'
  /v1/domains/{domain}:
    parameters:
    - $ref: '#/components/parameters/DomainName'
    get:
      tags:
      - Domains
      summary: Get a domain
      operationId: getDomain
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
    delete:
      tags:
      - Domains
      summary: Delete a domain
      operationId: deleteDomain
      responses:
        '200':
          description: Deleted
  /v1/domains/{domain}/verify:
    parameters:
    - $ref: '#/components/parameters/DomainName'
    get:
      tags:
      - Domains
      summary: Verify a domain
      description: Re-checks DNS records with the upstream ESP. If the domain is now verified, Mission Control atomically adds the new address as a primary alias on the inbox (preserving thread history). The first time this sees the domain verified, the `domain.verified` webhook fires.
      operationId: verifyDomain
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  domain:
                    type: string
                  status:
                    type: string
                    enum:
                    - pending
                    - verified
                    - failed
                  verified:
                    type: boolean
                  dnsRecords:
                    type: array
                    items:
                      $ref: '#/components/schemas/DnsRecord'
                  sharedWith:
                    type: string
                    nullable: true
                    enum:
                    - google
                    - microsoft
                    - other
                    - null
                  routingSteps:
                    type: array
                    items:
                      type: string
                    description: Present only for shared domains.
                  message:
                    type: string
  /v1/domains/{domain}/zone-file:
    parameters:
    - $ref: '#/components/parameters/DomainName'
    get:
      tags:
      - Domains
      summary: Export BIND zone file
      description: Returns the current DNS records as a BIND-format zone file fragment. Convenient for pasting into a DNS editor that accepts zone-file imports.
      operationId: getDomainZoneFile
      responses:
        '200':
          description: Zone file text
          content:
            text/plain:
              schema:
                type: string
components:
  schemas:
    DnsRecord:
      type: object
      properties:
        type:
          type: string
          enum:
          - TXT
          - CNAME
          - MX
          - A
        name:
          type: string
        value:
          type: string
        priority:
          type: integer
          nullable: true
        status:
          type: string
          enum:
          - pending
          - verified
          nullable: true
        note:
          type: string
          nullable: true
          description: 'Extra instructions for this record. On a shared domain the SPF

            record is merged into your existing one, and the note says to

            replace the existing record (a domain must have only one SPF record).

            '
    Domain:
      type: object
      properties:
        id:
          type: string
          format: uuid
        domain:
          type: string
        status:
          type: string
          enum:
          - pending
          - verified
          - failed
        workspaceId:
          type: string
          format: uuid
        dnsRecords:
          type: array
          items:
            $ref: '#/components/schemas/DnsRecord'
        sharedWith:
          type: string
          nullable: true
          enum:
          - google
          - microsoft
          - other
          - null
          description: 'Set when the domain''s main mail stays at another provider (Google

            Workspace, Microsoft 365 or other) and Anjal mailboxes live

            alongside it. Null for a domain whose MX points at Anjal.

            '
        routingSteps:
          type: array
          items:
            type: string
          description: 'Present only for shared domains. Steps for the provider''s admin

            console so it passes mail for addresses it doesn''t know on to Anjal.

            '
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
  parameters:
    DomainName:
      name: domain
      in: path
      required: true
      schema:
        type: string
        example: mail.example.com
      description: Fully-qualified domain name
  securitySchemes:
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'Any of `sa_`, `tk_`, `wk_`, `ak_`, or `mam_` keys. Scope is enforced

        per-endpoint — see individual operation descriptions.

        '