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

# Create handoff link

> Issues a single-use link that lets the contact continue the conversation on
a different channel while staying the same contact.

The source session supplies the contact and the channel being left; the
destination plugin connection decides which strategy builds the URL — a
`wa.me` deep link whose pre-filled text carries the token, a Telegram
`?start=` deep link, or a Web Chat URL with the token as a query param.

A Web Chat destination also seeds the contact's
`contactExternal['urn:talqui:chat-widget:0']` when it has none, and embeds
it in the link as `S_ID`, so the contact is recognized in whichever browser
they open it in. That is the only write this endpoint performs, it never
replaces an existing id, and it is skipped for every other channel — see
`CreateHandoffLinkUseCase.resolveShadowID`.

The link is only generated here. Delivering it to the contact — pasting it
into a reply, or sending it automatically — is the caller's job.



## OpenAPI

````yaml /api/services-api.yaml post /v1/tenants/{tenantID}/handoff-links
openapi: 3.0.3
info:
  description: >-
    Talqui is an omnichannel customer service platform that unifies
    conversations from WhatsApp, Instagram, Telegram, and many other channels
    into a single attendant panel, alongside a broader ecosystem of plugins and
    integrations. This API gives developers programmatic access to Talqui's
    services layer, exposing the operations needed to read and manage tenants,
    conversations, plugins, and related resources that power that platform.


    Authentication is available on behalf of an Operator (the default for this
    API), a Plugin Connection, or a Plugin — see the [Talqui authentication
    guide](https://docs.talqui.chat/guides/introduction/authentication/) for how
    each token is obtained and when to use it.


    Need help or have questions not covered here? Reach out to
    support@talqui.com.
  license:
    name: ISC
    url: https://opensource.org/license/isc-license-txt
  title: Talqui - Services API
  version: 0.69.1
servers:
  - description: Production
    url: https://services-api.talqui.chat
security: []
tags:
  - name: Tenants
  - name: Settings
  - name: Analytics/Reports
  - name: Analytics
  - name: Campaigns
  - name: Campaigns/Models
  - name: Contacts
  - name: Contacts/Imports
  - name: Handoff links
  - name: Inboxes
  - name: Messages
  - name: Notifications
  - name: Sessions
  - name: Operators/Shortcuts
  - name: Operators
  - name: Plugins
  - name: Setup
  - name: Uploads
paths:
  /v1/tenants/{tenantID}/handoff-links:
    post:
      tags:
        - Handoff links
      summary: Create handoff link
      description: >-
        Issues a single-use link that lets the contact continue the conversation
        on

        a different channel while staying the same contact.


        The source session supplies the contact and the channel being left; the

        destination plugin connection decides which strategy builds the URL — a

        `wa.me` deep link whose pre-filled text carries the token, a Telegram

        `?start=` deep link, or a Web Chat URL with the token as a query param.


        A Web Chat destination also seeds the contact's

        `contactExternal['urn:talqui:chat-widget:0']` when it has none, and
        embeds

        it in the link as `S_ID`, so the contact is recognized in whichever
        browser

        they open it in. That is the only write this endpoint performs, it never

        replaces an existing id, and it is skipped for every other channel — see

        `CreateHandoffLinkUseCase.resolveShadowID`.


        The link is only generated here. Delivering it to the contact — pasting
        it

        into a reply, or sending it automatically — is the caller's job.
      operationId: CreateHandoffLinkController
      parameters:
        - in: path
          name: tenantID
          required: true
          schema:
            description: The tenant the handoff link is issued for.
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            type: string
      requestBody:
        content:
          application/json:
            schema:
              properties:
                destinationPluginConnectionID:
                  description: Plugin connection of the channel the contact should move to.
                  minLength: 1
                  type: string
                expiresInSeconds:
                  description: How long the link stays redeemable. Defaults to 24h.
                  maximum: 604800
                  minimum: 300
                  type: integer
                sourceSessionID:
                  description: Session the contact is currently talking on.
                  minLength: 1
                  type: string
              required:
                - sourceSessionID
                - destinationPluginConnectionID
              type: object
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  handoffLink:
                    additionalProperties: false
                    properties:
                      contactID:
                        description: >-
                          The contact this link re-identifies on the destination
                          channel.
                        type: string
                      createdAt:
                        description: When the link was issued.
                        format: date-time
                        type: string
                      expiresAt:
                        description: >-
                          Instant after which the link can no longer be
                          redeemed.
                        format: date-time
                        type: string
                      handoffLinkID:
                        description: Unique identifier for this handoff link (UUIDv7).
                        type: string
                      handoffLinkToken:
                        description: Single-use bearer token carried by the generated URL.
                        type: string
                      handoffLinkURL:
                        description: >-
                          The channel-specific URL the contact opens to continue
                          elsewhere.
                        type: string
                      sourceChannel:
                        description: Channel URN the contact is currently talking on.
                        nullable: true
                        type: string
                      sourceSessionID:
                        description: The session the handoff was generated from.
                        type: string
                      status:
                        description: PENDING until redeemed; USED is terminal.
                        enum:
                          - PENDING
                          - USED
                        type: string
                      targetChannel:
                        description: Channel URN the contact is being sent to.
                        type: string
                      targetPluginConnectionID:
                        description: The destination plugin connection the link points at.
                        type: string
                      tenantID:
                        description: The tenant this link belongs to.
                        type: string
                      updatedAt:
                        description: When the link was last modified.
                        format: date-time
                        type: string
                      usedAt:
                        description: When the link was redeemed, if it was.
                        format: date-time
                        nullable: true
                        type: string
                      usedByContactID:
                        description: Contact the destination channel saw at redemption.
                        nullable: true
                        type: string
                      usedSessionID:
                        description: >-
                          Session created on the destination channel at
                          redemption.
                        nullable: true
                        type: string
                    required:
                      - handoffLinkID
                      - handoffLinkToken
                      - handoffLinkURL
                      - tenantID
                      - contactID
                      - sourceSessionID
                      - sourceChannel
                      - targetChannel
                      - targetPluginConnectionID
                      - status
                      - usedAt
                      - usedSessionID
                      - usedByContactID
                      - expiresAt
                      - createdAt
                      - updatedAt
                    type: object
                required:
                  - handoffLink
                type: object
          description: Response for status 201.
        '400':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      oneOf:
                        - $ref: '#/components/schemas/MissingTenantIDError'
                        - $ref: '#/components/schemas/RequestValidationError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            MissingTenantIDError (error_code 1001): tenantID absent from the
            URL. | RequestValidationError (error_code 1002): body or params fail
            schema validation.
        '401':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      $ref: '#/components/schemas/UnauthorizedError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: 'UnauthorizedError (error_code 1003): missing/invalid operator token.'
        '403':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      $ref: '#/components/schemas/ForbiddenError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            ForbiddenError (error_code 1004): operator does not belong to this
            tenant, the caller's plugin connection belongs to another tenant, or
            the caller authenticated with a bare `Plugin` credential (no tenant
            binding — issue links as the connection).
        '404':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      oneOf:
                        - $ref: '#/components/schemas/TenantNotFoundError'
                        - $ref: '#/components/schemas/SessionNotFoundError'
                        - $ref: '#/components/schemas/PluginConnectionNotFoundError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            TenantNotFoundError (error_code 1062): tenantID does not resolve to
            a tenant. | SessionNotFoundError (error_code 1060): sourceSessionID
            does not resolve within this tenant. | PluginConnectionNotFoundError
            (error_code 1054): destinationPluginConnectionID does not resolve
            within this tenant.
        '409':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      $ref: '#/components/schemas/HandoffLinkTokenCollisionError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            HandoffLinkTokenCollisionError (error_code 1604): generated token
            collided with a live one.
        '422':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      oneOf:
                        - $ref: '#/components/schemas/HandoffLinkSameChannelError'
                        - $ref: >-
                            #/components/schemas/HandoffLinkChannelUnsupportedError
                        - $ref: >-
                            #/components/schemas/HandoffLinkTargetMisconfiguredError
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            HandoffLinkSameChannelError (error_code 1605): destination is the
            connection the session already runs on. |
            HandoffLinkChannelUnsupportedError (error_code 1602): destination
            channel has no handoff-link strategy. |
            HandoffLinkTargetMisconfiguredError (error_code 1603): destination
            connection lacks a number/bot/URL to point at.
        '500':
          content:
            application/json:
              schema:
                properties:
                  errors:
                    items:
                      $ref: '#/components/schemas/UnknownError'
                    maxItems: 1
                    minItems: 1
                    type: array
                required:
                  - errors
                type: object
          description: >-
            UnknownError (error_code 5999): Unexpected internal error not
            otherwise documented for this endpoint.
components:
  schemas:
    MissingTenantIDError:
      additionalProperties: false
      properties:
        error:
          enum:
            - MissingTenantIDError
          type: string
        error_code:
          enum:
            - 1001
          type: number
        message:
          example: tenantID is required.
          type: string
        statusCode:
          enum:
            - 400
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    RequestValidationError:
      additionalProperties: false
      properties:
        error:
          enum:
            - RequestValidationError
          type: string
        error_code:
          enum:
            - 1002
          type: number
        fields:
          items:
            additionalProperties: false
            properties:
              allowed:
                type: string
              field:
                type: string
              received:
                type: string
            required:
              - field
              - received
              - allowed
            type: object
          type: array
        message:
          example: Invalid request data.
          type: string
        statusCode:
          enum:
            - 400
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
        - fields
      type: object
    UnauthorizedError:
      additionalProperties: false
      properties:
        error:
          enum:
            - UnauthorizedError
          type: string
        error_code:
          enum:
            - 1003
          type: number
        message:
          example: You are not authorized to perform this request.
          type: string
        statusCode:
          enum:
            - 401
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    ForbiddenError:
      additionalProperties: false
      properties:
        error:
          enum:
            - ForbiddenError
          type: string
        error_code:
          enum:
            - 1004
          type: number
        message:
          example: You do not have access to this resource.
          type: string
        statusCode:
          enum:
            - 403
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    TenantNotFoundError:
      additionalProperties: false
      properties:
        error:
          enum:
            - TenantNotFoundError
          type: string
        error_code:
          enum:
            - 1062
          type: number
        message:
          example: Tenant not found.
          type: string
        statusCode:
          enum:
            - 404
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    SessionNotFoundError:
      additionalProperties: false
      properties:
        error:
          enum:
            - SessionNotFoundError
          type: string
        error_code:
          enum:
            - 1060
          type: number
        message:
          example: Session not found.
          type: string
        statusCode:
          enum:
            - 404
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    PluginConnectionNotFoundError:
      additionalProperties: false
      properties:
        error:
          enum:
            - PluginConnectionNotFoundError
          type: string
        error_code:
          enum:
            - 1054
          type: number
        message:
          example: Plugin connection not found.
          type: string
        statusCode:
          enum:
            - 404
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    HandoffLinkTokenCollisionError:
      additionalProperties: false
      properties:
        error:
          enum:
            - HandoffLinkTokenCollisionError
          type: string
        error_code:
          enum:
            - 1604
          type: number
        message:
          example: Could not allocate a handoff link token. Try again.
          type: string
        statusCode:
          enum:
            - 409
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    HandoffLinkSameChannelError:
      additionalProperties: false
      properties:
        error:
          enum:
            - HandoffLinkSameChannelError
          type: string
        error_code:
          enum:
            - 1605
          type: number
        message:
          example: The destination connection is the one this session already runs on.
          type: string
        statusCode:
          enum:
            - 422
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    HandoffLinkChannelUnsupportedError:
      additionalProperties: false
      properties:
        error:
          enum:
            - HandoffLinkChannelUnsupportedError
          type: string
        error_code:
          enum:
            - 1602
          type: number
        message:
          example: This channel cannot receive handoff links.
          type: string
        statusCode:
          enum:
            - 422
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    HandoffLinkTargetMisconfiguredError:
      additionalProperties: false
      properties:
        error:
          enum:
            - HandoffLinkTargetMisconfiguredError
          type: string
        error_code:
          enum:
            - 1603
          type: number
        message:
          example: The destination channel is not configured for handoff links.
          type: string
        statusCode:
          enum:
            - 422
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object
    UnknownError:
      additionalProperties: false
      properties:
        error:
          enum:
            - UnknownError
          type: string
        error_code:
          enum:
            - 5999
          type: number
        message:
          example: For some unknown reason your request has not succeeded.
          type: string
        statusCode:
          enum:
            - 500
          type: number
      required:
        - statusCode
        - error
        - error_code
        - message
      type: object

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.