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

# Get a one-time link to the IVR designer

> For an application that manages IVRs through the API and lets its own users design them. An IVR is drawn in FireTone's designer, not posted as JSON; this returns a URL that opens that designer on this one IVR, with no FireTone sign-in.

Call it from your server (it needs your API key), then open `url` in a popup from the page at `return_origin`.

**The link works once.** The designer trades it for a session as it loads. That session can read, save and publish this IVR, and read the lists its steps choose from (agents, queues, extensions, recordings, templates). It can do nothing else, and it ends at `expires_at`, when the user presses Done, or when the API key that asked for it is revoked.

**What the designer tells your page.** It posts messages to the window that opened it, addressed to `return_origin`:

`{"source":"firetone","type":"ivr.saved" | "ivr.published" | "ivr.closed","flow_id":"…","name":"…","draft_revision":3,"published_revision":2}`

Check `event.origin` is your FireTone panel's address before trusting one, and read the IVR back with `GET /ivr-flows/{id}` rather than acting on the message alone.

409 `no_panel_address` when the platform has no panel address to open.



## OpenAPI

````yaml /api-reference/openapi-public.json post /ivr-flows/{id}/designer-session
openapi: 3.0.3
info:
  description: >-
    The API your own systems use: place and follow calls, have an AI agent call
    someone, run campaigns and get their results, keep contacts in step with
    your CRM, and receive signed webhooks. Authenticate with an integration key
    (Authorization: Bearer ft_...), used only from the IP addresses it allows.
  title: FireTone API
  version: 0.1.0
servers:
  - description: Your platform's API host
    url: https://{host}/api/v1
    variables:
      host:
        default: api.firet.one
security:
  - bearerAuth: []
tags:
  - name: Auth
  - description: >-
      Live calls and what can be done to them: hang up, hold, transfer, park,
      merge, monitor, whisper; the Desk's own call.
    name: Calls
  - description: 'Outbound campaigns: contacts, attempts, outcomes.'
    name: Campaigns
  - description: 'Customers: who called, what is known about them, and their memory.'
    name: Contacts
  - description: What was said on an AI call, and the review of it.
    name: Conversations
  - name: IVR
  - description: >-
      Your own systems: HTTP connections an IVR calls mid-call, and webhooks for
      call events. Tenant URLs must be public https addresses.
    name: Integrations
  - name: Live
  - name: Provisioning
  - name: Reporting
  - description: Tickets raised by people, agents and the API.
    name: Tickets
  - description: Messages left for an extension or a queue.
    name: Voicemail
paths:
  /ivr-flows/{id}/designer-session:
    post:
      tags:
        - IVR
      summary: Get a one-time link to the IVR designer
      description: >-
        For an application that manages IVRs through the API and lets its own
        users design them. An IVR is drawn in FireTone's designer, not posted as
        JSON; this returns a URL that opens that designer on this one IVR, with
        no FireTone sign-in.


        Call it from your server (it needs your API key), then open `url` in a
        popup from the page at `return_origin`.


        **The link works once.** The designer trades it for a session as it
        loads. That session can read, save and publish this IVR, and read the
        lists its steps choose from (agents, queues, extensions, recordings,
        templates). It can do nothing else, and it ends at `expires_at`, when
        the user presses Done, or when the API key that asked for it is revoked.


        **What the designer tells your page.** It posts messages to the window
        that opened it, addressed to `return_origin`:


        `{"source":"firetone","type":"ivr.saved" | "ivr.published" |
        "ivr.closed","flow_id":"…","name":"…","draft_revision":3,"published_revision":2}`


        Check `event.origin` is your FireTone panel's address before trusting
        one, and read the IVR back with `GET /ivr-flows/{id}` rather than acting
        on the message alone.


        409 `no_panel_address` when the platform has no panel address to open.
      operationId: postIvrFlowsIdDesignerSession
      parameters:
        - in: path
          name: id
          required: true
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDesignerSessionRequest'
        required: true
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DesignerSession'
          description: The link
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
components:
  schemas:
    CreateDesignerSessionRequest:
      properties:
        panel_origin:
          description: >-
            Rarely needed. Which of the platform's panel addresses the link
            should open, when it has several; it must be one of them.
          type: string
        return_origin:
          description: >-
            The origin of the page that opens the designer: scheme, host and
            port, nothing else. The designer reports to this origin only
            (`postMessage`), so it must be exact. `https`, or `http://localhost`
            while developing; an origin the platform already admits browsers
            from is accepted as it is listed. A wildcard is refused.
          example: https://app.example.com
          type: string
        ttl_minutes:
          default: 120
          description: How long the link, and the session it becomes, lasts.
          maximum: 480
          minimum: 5
          type: integer
      required:
        - return_origin
      type: object
    DesignerSession:
      properties:
        expires_at:
          description: When the link, and the designer session, stops working.
          format: date-time
          type: string
        flow_id:
          description: The IVR the designer will open.
          format: uuid
          type: string
        url:
          description: >-
            Open this in a popup (`window.open`). It works once: the designer
            trades it for a session as it loads, so a copied or reloaded link
            shows an expired message. The secret is in the URL fragment and is
            never sent to a server.
          type: string
      required:
        - url
        - flow_id
        - expires_at
      type: object
    Error:
      properties:
        error:
          properties:
            code:
              enum:
                - invalid_request
                - invalid_credentials
                - unauthenticated
                - forbidden
                - not_found
                - conflict
                - internal
              type: string
            message:
              type: string
          required:
            - code
            - message
          type: object
      required:
        - error
      type: object
  responses:
    BadRequest:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: The request is not valid. The message names every field that is wrong.
    Unauthorized:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Missing, invalid or expired token
    Forbidden:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Outside the caller's scope, or insufficient role
    NotFound:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Not found, or not visible to the caller
    Conflict:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: >-
        The row is still referenced, or a unique value is taken. The message
        names what still points at it.
  securitySchemes:
    bearerAuth:
      bearerFormat: JWT or API key
      description: >-
        Every request sends `Authorization: Bearer <token>`. The token is either
        a panel session (a JWT from /auth/login, 12 hours) or an API key
        `ft_<id>_<secret>`. An API key is accepted only from an address on its
        IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required
        for an old key that has none), is limited to its rate per minute (429
        rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on
        every response), and at most 60 call placements a minute.
      scheme: bearer
      type: http

````

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