> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hookmyapp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create onboarding link

> Create a link you can send to a customer so they can connect their
WhatsApp number or Instagram account. Links stay listed and copyable.




## OpenAPI

````yaml api-reference/openapi.yaml POST /org/onboarding-links
openapi: 3.1.0
info:
  title: HookMyApp API
  version: 1.0.0
  description: >
    The HookMyApp REST API. Everything the dashboard and the CLI can do, your

    code and your AI agents can do too.


    ## Authentication


    Two kinds of Bearer credentials exist. Do not mix them up:


    - **API key (`hmok_...`)** authenticates *you* (or your agent) to
      `https://api.hookmyapp.com`.
      Create one in the dashboard under **Org → API keys → Create API Key**
      (full org access, optional expiration date, reveal or
      revoke it any time from that page), or without a
      browser via the [agent auth flow](#tag/agent-auth) described in
      [`GET /auth.md`](https://api.hookmyapp.com/auth.md). That flow can mint a
      scope-limited key. Send it as `Authorization: Bearer hmok_...`, or on
      `/mcp` as `X-API-Key: hmok_...`. Use it for org, workspace, customer,
      channel and webhook management.
    - **Channel token (`hmat_...`)** sends messages from one connected channel.
      Mint it with `GET /meta/channels/{id}/token`. It is not valid on
      `https://api.hookmyapp.com`.

    Browser sessions from the dashboard use the same endpoints with a session

    cookie or WorkOS JWT instead of `hmok_`.


    ## IDs


    Every ID on the wire is a typed public ID, never an internal UUID:

    `ws_` workspace/customer, `ch_` channel, `org_` organization,

    `cred_` connection credential, `ac_` agent credential.


    ## Workspace context


    Workspace-scoped routes (channels, webhook config, and deliveries) resolve
    the

    workspace from the `X-Workspace-Id: ws_XXXXXXXX` header.


    ## Errors


    Errors return a stable machine-readable `code` plus a human `message`.

    Agent tokens that exceed their granted scopes get `403
    AGENT_SCOPE_INSUFFICIENT`.


    ## Webhook signatures


    Deliveries to your webhook are signed with `X-HookMyApp-Signature-256`,

    an HMAC-SHA256 of the raw body keyed on the channel `WEBHOOK_HMAC_SECRET`.

    The Verify Token is a separate value used only for the GET ownership probe.
servers:
  - url: https://api.hookmyapp.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Agent auth
    description: Register an agent credential (`hmok_`) with email OTP, no browser needed
  - name: Organizations
    description: Your organization and its summary
  - name: Customers
    description: Customer workspaces you run messaging for (SaaS Mode)
  - name: Onboarding links
    description: Links your customers open to connect their own channels
  - name: Workspaces
    description: Team workspaces
  - name: Channels
    description: Connected WhatsApp and Instagram channels, and their channel tokens
  - name: Webhook config
    description: Where inbound messages are delivered for a channel
  - name: Deliveries
    description: Inspect inbound delivery logs and app responses
paths:
  /org/onboarding-links:
    post:
      tags:
        - Onboarding links
      summary: Create onboarding link
      description: |
        Create a link you can send to a customer so they can connect their
        WhatsApp number or Instagram account. Links stay listed and copyable.
      operationId: createOnboardingLink
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channelType
                - label
              properties:
                channelType:
                  type: string
                  enum:
                    - whatsapp
                    - instagram
                label:
                  type: string
                  maxLength: 80
                targetWorkspaceId:
                  type: string
                  description: >-
                    Existing customer (`ws_`) the connect lands in. Omit to
                    create a new customer on connect.
                workspaceName:
                  type: string
                  maxLength: 80
                externalId:
                  type: string
                  maxLength: 120
                destinationWebhookUrl:
                  type: string
                  format: uri
                  maxLength: 2048
                destinationVerifyTokenOverride:
                  type: string
                  maxLength: 256
                connectedNotificationUrl:
                  type: string
                  format: uri
                  description: >
                    Where we notify you when the customer's connect finishes. We
                    POST a

                    signed `channel.connected` event on success or
                    `channel.connect_failed`

                    on failure — never mixed into your message destination
                    webhook.
                successRedirectUrl:
                  type: string
                  format: uri
                  description: >
                    Where we send the customer's browser after a successful
                    connect. We

                    append `status=completed`, `phone_number_id`,
                    `display_phone_number`,

                    and `externalId` when set.
                failureRedirectUrl:
                  type: string
                  format: uri
                  description: >
                    Where we send the customer's browser after a failed connect.
                    We append

                    `status=failed`, `externalId` when set, and `reason`, one
                    of:

                    `customer_cancelled`, `permission_denied`,
                    `already_connected`,

                    `no_number_available`, `link_inactive`, `temporary_failure`,

                    `service_error`.
                verifyToken:
                  type: string
                  maxLength: 120
      responses:
        '201':
          description: Link created
          content:
            application/json:
              schema:
                type: object
                properties:
                  publicId:
                    type: string
                  url:
                    type: string
                    format: uri
                  token:
                    type: string
                  verifyToken:
                    type:
                      - string
                      - 'null'
                  notificationSigningSecret:
                    type:
                      - string
                      - 'null'
                    description: >
                      Signs `connectedNotificationUrl` deliveries. Verify the

                      `X-HookMyApp-Signature-256` header against this secret
                      (HMAC-SHA256

                      over the raw request body). Minted on every new link —
                      `null` only on

                      links created before signing secrets existed. To get one,
                      regenerate

                      the link (note: regenerating also rotates the connect
                      URL).
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Error'
components:
  responses:
    BadRequest:
      description: >-
        Bad request. Business-rule rejections use the `Error` shape;
        request-body validation failures use the `ValidationError` shape.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/Error'
              - $ref: '#/components/schemas/ValidationError'
    Error:
      description: Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      properties:
        statusCode:
          type: integer
          example: 403
          description: HTTP status code
          mirrors the response status: null
        code:
          type: string
          description: Stable machine-readable code, e.g. `AGENT_SCOPE_INSUFFICIENT`
        message:
          type: string
        requestId:
          type: string
          description: Include this when contacting support about the request
    ValidationError:
      type: object
      description: |
        Returned when the request body fails validation (400). `message` is an
        array with one entry per failed rule.
      properties:
        message:
          type: array
          items:
            type: string
          description: One human-readable message per failed validation rule
        error:
          type: string
          example: Bad Request
        statusCode:
          type: integer
          example: 400
        requestId:
          type: string
          description: Include this when contacting support about the request
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: '`Authorization: Bearer hmok_...` API key or a dashboard session token'

````