CustomDomain Sharing API

Forward-to-a-colleague share links that resume a connect flow from a server-side prefill.

GitHub OpenAPI

Operations 2

POST /sharing/connect Mint a share link that resumes a connect flow from a server-side prefill #
GET /share/{token} Read a share link's server-side prefill (token is the bearer credential) #

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/customdomain-ai:customdomain-ai-sharing-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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

customdomain-ai-sharing-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Customdomain Ai Sharing API
  version: '1.0'
  license:
    name: Apache-2.0
    identifier: Apache-2.0
  description: 'Operations tagged Sharing across 2 of this provider''s published API definitions: customdomain-ai-openapi-original.json, customdomain-ai-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.customdomain.ai/v1
  description: Hosted control-plane
- url: http://localhost:8080/v1
  description: Local control-plane
security:
- apiKey: []
tags:
- name: Sharing
  description: Forward-to-a-colleague share links that resume a connect flow from a server-side prefill.
paths:
  /sharing/connect:
    post:
      operationId: createSharedFlow
      summary: Mint a share link that resumes a connect flow from a server-side prefill
      tags:
      - Sharing
      description: '**Forward-to-a-colleague.** The widget posts the domain(s) a teammate should connect plus an arbitrary `prefill` (the widget config to resume); the control plane stores that server-side under an opaque token and returns the absolute `link` (ends `/share/{token}`) and a `job_id`. Every domain the link targets must be one the calling token may act on: a share link never widens a domain-bound token''s reach, so such a token must name a `domain` or `domains` (`400` otherwise). The link''s server-side prefill is read back by `GET /share/{token}`, which the teammate''s widget bootstraps from instead of a URL that carries the config in the clear. The share token EXPIRES 30 days after it is minted. **Share by email.** With `recipient_email`, CustomDomain™ also emails the link to that address together with the DNS records the domain needs (the records of this application''s connection for it when one exists, otherwise the default set) and your optional `note`. The email names the application as the product that asked. A malformed address is a `400` `invalid_request`; so is `recipient_email` without a `domain` or `domains`. Sending is capped at 3 emails per domain per application and 50 per application every 24 hours, and 10 per client IP address per hour; over any cap the call is a `429` `rate_limited` and no link is minted. When the email cannot be sent the link is still returned, with `email_error: "not_sent"`, so the widget can offer to copy it instead. **From a server.** An API key mints a link for `application_id` (an application in the key''s tenant, 404 otherwise) or for the key''s own application, and the teammate''s flow then connects the domain into that application. A member''s key needs a role that operates apps. A widget JWT always shares for its own application. **Whose end user the recipient is.** The recipient''s widget token names the end user the sharer is, recorded when the link is made and never worked out from the domain when it is opened. A widget token shares as its own `end_user_ref` (none when it names none). An API key or an app-scoped token shares as the `end_user_ref` of the application''s connection for the one domain the link names, and as no end user when there is no connection for it yet, the connection names none, or the link names several domains or none. A connection that names an end user answers only a widget token that names the same one, so this is what lets the recipient reach the connection the link was made for; another end user who shares this one''s domain gets a token that names itself and is still refused on it. A `prefill` key the control plane records this in is dropped from the caller''s prefill and never read back. **What a widget token may share.** Only a domain whose connection in the application its own token owns, by the rule every managed DNS route applies: a connection that names the token''s `end_user_ref`; one that names no end user when the token is bound to that domain; and, for a token that names no end user and is bound to no domain, the connection of the domain it is asked about, since it counts only that connection as its own (a token with an `end_user_ref` and no bound domain owns none that names no end user). Any other shared domain is refused with `403` `token_wrong_end_user` and no link is made. A domain with no connection yet can be shared. **What the recipient owns and reaches.** One rule: the recipient owns exactly what the sharer owned, and may touch only the shared domains. It owns the sharer''s end user and the domains the sharer''s own binding made its own, recorded when the link is made, and never the shared domains it is bound to: a link made by a token that names no end user and is bound to none owns each connection by itself, so a zone that links several of the shared domains is not its own.'
      security:
      - widgetJWT: []
      - apiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                application_id:
                  type: string
                  description: With an API key, the application the shared flow connects into (defaults to the key's own). A widget JWT may only omit it or send its own application id (403 otherwise).
                domain:
                  type: string
                  description: Single domain the teammate will connect.
                domains:
                  type: array
                  items:
                    type: string
                  description: Several domains the teammate will connect.
                prefill:
                  type: object
                  additionalProperties: true
                  description: Arbitrary widget config to resume. Served back by `GET /share/{token}`, with its `domain` and `domains` keys replaced by the normalized targets from `domain` and `domains` above.
                recipient_email:
                  type: string
                  format: email
                  maxLength: 254
                  description: Optional. One plain address (no display name, no list) to email the link and the DNS records to, for example the person who runs the domain's DNS. The instructions cover `domain`, or the first of `domains`.
                note:
                  type: string
                  maxLength: 280
                  description: Optional plain-text note quoted in the email (at most 280 characters; markup is shown as text, never interpreted). Ignored without `recipient_email`.
      responses:
        '201':
          description: Share link minted (and, with recipient_email, the email attempted)
          content:
            application/json:
              schema:
                type: object
                required:
                - link
                - job_id
                properties:
                  link:
                    type: string
                    format: uri
                    description: Absolute forwardable URL ending /share/<token>.
                  job_id:
                    type: string
                  emailed_to:
                    type: string
                    format: email
                    description: Present when the email was accepted for delivery; the normalized recipient address.
                  email_error:
                    type: string
                    enum:
                    - not_sent
                    description: Present when recipient_email was given but the email could not be sent. The link is still valid.
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          description: '`forbidden` (the token is bound to a different domain, or its role cannot write connections) or `token_wrong_end_user` (a widget token that does not own the connection of a domain it asks to share: the connection names another end user, or names none while the token is not bound to that domain; no link is made).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/Error'
        '429':
          description: Share-by-email cap reached (`rate_limited`); no link was minted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    servers:
    - url: https://api.customdomain.ai/v1
      description: Hosted control-plane
    - url: http://localhost:8080/v1
      description: Local control-plane
  /share/{token}:
    servers:
    - url: https://api.customdomain.ai
      description: Hosted control-plane (share links are not under /v1)
    - url: http://localhost:8080
      description: Local control-plane
    get:
      operationId: getSharedFlow
      summary: Read a share link's server-side prefill (token is the bearer credential)
      tags:
      - Sharing
      description: 'Public by design: the opaque, unguessable `token` in the path IS the credential (mirroring the OAuth callback''s state-as-credential pattern), so no bearer is required. Returns the stored `prefill` so a teammate''s widget can resume the flow. A share token EXPIRES 30 days after it is minted; unknown OR expired tokens are indistinguishable and both return `404`.


        The `widget_token` it answers reaches only the shared domains (intersected with the sharer''s binding when the sharer was bound) and owns exactly what the sharer owned: the sharer''s end user and the domains the sharer''s own binding made its own, never the shared domains it is bound to (`createSharedFlow`). So it sets the connection the link was made for up as the end user would (the secure sign in, managed DNS, a nameserver move, the monitoring reads), and acts on a connection that names no end user only when the sharer could have (a link from a sharer that names no end user and is bound to no domain may start the secure sign in on the connection it owns, the one a call is about). A link made before the sharer was recorded (before this release), and one whose shared domains are all outside the sharer''s binding, mints no `widget_token`; the first is answered `404` like an expired link, whose answer the console words as "invalid or expired, ask for a fresh link". A reconnect link is the end user''s own and keeps working, except that one whose key was replaced by another, or whose connection is gone, now answers `404` too (it used to open the plain flow). A share link is a credential for that one setup, not the end user, so it never removes the domain (`403` `forbidden`, on every connection), never sets the end user''s contact address (on any connection), is never offered the end user''s kept keys (saved access) and is not the maker of a key as the end user: a key made at a sign in it starts is not the end user''s own for saved access, and is never swapped into a key row the end user already keeps, and its sign in is never in reconnect mode (`403` `forbidden`: it cannot renew the end user''s key). A reconnect link''s token stays the end user.'
      security: []
      parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Shared flow prefill
          content:
            application/json:
              schema:
                type: object
                required:
                - token
                - application_id
                - job_id
                - prefill
                properties:
                  token:
                    type: string
                  application_id:
                    type: string
                  job_id:
                    type: string
                  prefill:
                    type: object
                    additionalProperties: true
        '404':
          $ref: '#/components/responses/Error'
components:
  responses:
    Error:
      description: 'Error envelope: {code, title, details}.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      required:
      - code
      - title
      properties:
        code:
          type: string
          description: stable machine code, e.g. invalid_request, unauthorized, not_found, service_unavailable, internal_error
        title:
          type: string
          description: human-readable summary
        details:
          type: string
          description: optional extra context
  securitySchemes:
    managementKey:
      type: http
      scheme: bearer
      description: Operator management key. Gates provisioning + reads across tenants.
    provisionSecret:
      type: apiKey
      in: header
      name: X-Provision-Secret
      description: Narrow secret held by the customer console's server, for signup provisioning and its other server to server calls (also accepted as a Bearer token, except on POST /audit, where a Bearer is always a tenant credential).
    apiKey:
      type: http
      scheme: bearer
      description: Long-lived tenant API key, cd_live_… (production) or cd_test_… (staging, development), stored hashed. Keys created before the switch to cd_ start with sk_live_… / sk_test_… and are still accepted. Scoped to the owning application's tenant.
    widgetJWT:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Short-lived widget JWT minted server-side via POST /v1/tokens. Scoped to one application (and optionally one domain).
    browserSession:
      type: http
      scheme: bearer
      description: The session token of one cloud browser sign in, returned by `POST /browser-sessions:redeem`. It binds one session, one connection and one workspace, lives at most as long as the session, and stops working the moment the session ends. It lives in the popup's memory only.
    agentToken:
      type: oauth2
      description: 'A delegated agent access token: an ES256 JWT that lives 15 minutes, sent as a bearer token. A person approves an agent in the console and the agent exchanges its authorization code (with PKCE) at the token endpoint. The token carries the workspace, the one application the grant is bound to, the grant, the approved scopes and its audience, and is verified against `/.well-known/jwks.json`. A route that accepts it lists the scope it needs; a token without the scope is refused with `403`, and a connection or order of another application answers `404`.'
      flows:
        authorizationCode:
          authorizationUrl: https://api.customdomain.ai/oauth/agent/authorize
          tokenUrl: https://api.customdomain.ai/oauth/agent/token
          refreshUrl: https://api.customdomain.ai/oauth/agent/token
          scopes:
            domains:read: Look at availability, connections, records, certificates, diagnoses, orders and the billing standing.
            domains:connect: Connect a domain, recheck it, reapply its records and start a one click setup.
            domains:disconnect: Remove a connected domain. The call must name the domain.
            domains:purchase: Buy a domain, up to the grant's monthly spend cap.
x-refined-from:
- customdomain-ai-openapi-original.json
- customdomain-ai-openapi.yml