openapi: 3.1.0
info:
  title: Txt2Voice API
  description: >
    # Txt2Voice API


    Text-to-speech delivered as phone calls. Submit a text message and a
    recipient phone number. The service converts the text to natural-sounding
    speech and delivers it as an automated voice call.


    ## Base URL


    All endpoints are relative to:


    `https://api.spirius.com/txt2voice`


    ## Features


    - Natural-sounding speech in 43 languages across 45 BCP 47 tags (English and
    Portuguese each have two regional variants)

    - Automatic translation: text written in another language is spoken in the
    selected `language` (e.g. Swedish text with `language: de` is read in
    German)

    - Delivery style controlled with plain-language `instructions`; no markup
    needed

    - Male or female voice selection via the `gender` field

    - Message status queries: `GET /v1/messages/{transaction_id}`

    - Automatic reattempts on unanswered or busy calls (final delivery report
      after the last attempt. Allow around 5 minutes before treating a message as
      undelivered)
    - Optional voicemail detection (`machine_detection`, default off): playback
      waits for an answering-machine greeting to end so the recorded message is
      complete; adds a few seconds of delay after a human answers
    - Delivery report webhooks with retry

    - Simple HTTP endpoint for easy integration


    ## Caller ID


    The caller ID (`sender`) shown to the recipient is resolved from the first

    available of these, in priority order:


    1. The `sender` field in the request.

    2. The account's configured default sender.

    3. The Spirius default sender ID.


    So a request that sends no `sender` and whose account has no configured
    default

    falls back to the Spirius default sender ID. Caller IDs are phone numbers in

    strict E.164 format. Client-specific caller IDs (the request `sender` and
    the

    account's configured default) **must be registered with Spirius before
    use**;

    the Spirius default sender ID is always available and needs no registration.
    A request

    with an unregistered sender is rejected immediately (HTTP 422 on the REST

    API, `request=false` on the Simple HTTP interface) with instructions. To

    register a number, contact Spirius support. Alphanumeric SMS sender IDs
    cannot be

    used for voice calls. For calls to Swedish destinations, the caller ID must

    additionally be a number provisioned through Spirius (Swedish anti-spoofing

    regulation); contact support to set one up.


    ## Voice Instructions (REST)


    The REST endpoint does **not** use SSML or prosody markup. Instead, pass an

    optional `instructions` field describing in plain language *how* the text

    should be spoken:


    ```json

    {
      "to": "+46701234567",
      "text": "Välkommen till Spirius! Vi ser fram emot att hjälpa dig.",
      "language": "sv",
      "instructions": "Läs med en varm och välkomnande röst."
    }

    ```


    More examples:


    A one-time code, read slowly and clearly:


    ```json

    {
      "to": "+46701234567",
      "text": "Din pin kod är 7 9 5 2",
      "language": "sv",
      "instructions": "Read the code slowly and clearly."
    }

    ```


    A service alert:


    ```json

    {
      "to": "+46701234567",
      "text": "This is an alert. Your server is unreachable.",
      "language": "en-US",
      "instructions": "Speak in a professional tone."
    }

    ```


    The `instructions` may be written in any language and are interpreted

    independently of the message language; they shape *how* the message is
    spoken

    and are **not read aloud**. Put the words you want spoken in `text`; keep

    `instructions` to delivery guidance only. For the most reliable results we

    recommend writing instructions in English or in the message's spoken
    language.


    Keep instructions simple and focused on delivery, as in the examples above.

    A single clear direction works best; stacking several emotional cues tends
    to

    push the voice too far and sound unnatural. Start from a tested phrasing and

    experiment from there.


    ## Reading Numbers and Codes


    How you separate numbers controls how they are read.


    **One digit at a time** (codes, PINs). Separate the digits with spaces:


    - `"Your code is 4 7 1 1"` → *"four, seven, one, one"*

    - `"Your code is 4711"` (no separators) → *"four thousand seven hundred
    eleven"*


    **Mixing single and multi-digit numbers.** Separate every value with a comma
    and

    a space. A space alone is enough when every value is a single digit, but
    with

    multi-digit values a comma is needed so they are read as whole numbers
    rather

    than individual digits. Comma-and-space always works, so prefer it:


    - `"2, 4, 98, 25"` → *"two, four, ninety-eight, twenty-five"*


    **Decimal numbers.** Always use a dot as the decimal separator; it is read

    correctly regardless of the selected language (a comma would be treated as a

    value separator instead):


    - `"2.50, 7.95, 4.55"` → *"two point five zero, seven point nine five, four
    point five five"*


    ## Supported Languages


    The REST `language` field takes a BCP 47 tag. English and Portuguese require

    the regional variant (`en-US`/`en-GB`, `pt-PT`/`pt-BR`); every other
    language

    uses the bare tag. Codes are case-insensitive and the default is `en-GB`.


    `language` selects the **spoken** language. Text written in a different
    language

    is automatically translated to it before it is spoken — for example

    `"Välkommen till Spirius"` with `language: de` is read in German

    (*"Willkommen bei Spirius"*).


    Automatic translation can be imperfect and may reword, alter, or drop
    details —

    **including digits**. For messages that must be delivered exactly — PIN
    codes,

    one-time passwords, reference numbers — write the text in the intended
    spoken

    language and set `language` to match, so the wording and numbers are
    preserved.

    Translation is a convenience for free-form text, not a guarantee of
    accuracy.


    | Code | Language |

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

    | `af` | Afrikaans |

    | `ar` | Arabic |

    | `bg` | Bulgarian |

    | `bn` | Bengali |

    | `ca` | Catalan |

    | `cs` | Czech |

    | `da` | Danish |

    | `de` | German |

    | `el` | Greek |

    | `en-GB` | English (UK) |

    | `en-US` | English (US) |

    | `es` | Spanish |

    | `et` | Estonian |

    | `fi` | Finnish |

    | `fr` | French |

    | `gu` | Gujarati |

    | `he` | Hebrew |

    | `hi` | Hindi |

    | `hr` | Croatian |

    | `hu` | Hungarian |

    | `id` | Indonesian |

    | `it` | Italian |

    | `ja` | Japanese |

    | `ko` | Korean |

    | `lt` | Lithuanian |

    | `lv` | Latvian |

    | `mr` | Marathi |

    | `ms` | Malay |

    | `nl` | Dutch |

    | `no` | Norwegian |

    | `pl` | Polish |

    | `pt-BR` | Portuguese (Brazil) |

    | `pt-PT` | Portuguese (Portugal) |

    | `ro` | Romanian |

    | `ru` | Russian |

    | `sk` | Slovak |

    | `sl` | Slovenian |

    | `sv` | Swedish |

    | `ta` | Tamil |

    | `te` | Telugu |

    | `th` | Thai |

    | `tr` | Turkish |

    | `uk` | Ukrainian |

    | `vi` | Vietnamese |

    | `zh` | Chinese |


    ## Authentication


    The account must have the Txt2Voice API permission enabled.


    - **`POST /v1/messages`** (REST): HTTP Basic Auth (Authorization header).

    - **`GET /v1/sendtts`** (Simple HTTP): credentials via `User` and `Pass`
    query parameters.


    ## Message Flow


    1. Submit message via `POST /v1/messages` or `GET /v1/sendtts`

    2. Receive `202 Accepted` with a `TransactionId`

    3. Service generates audio, initiates phone call

    4. On call completion, delivery report sent to configured webhook URL


    Unanswered or busy calls are automatically reattempted (2 reattempts at

    2-minute intervals); the delivery report is sent once, after the final

    attempt.


    ## Message Status


    `GET /v1/messages/{transaction_id}` returns the current state of a

    transaction (Basic auth, same credentials as sending):


    ```json

    {
      "transaction_id": "fa7a2ada-d3d4-4c93-a125-fec9ecd84f92",
      "status": "completed",
      "created": "2026-07-13T09:36:08Z",
      "duration_seconds": 24,
      "failure_code": null
    }

    ```


    `status` is `accepted` (in progress, including the retry window),

    `completed` (delivered; `duration_seconds` set) or `failed`

    (`failure_code` set: `1` = call could not be delivered, `5` = internal

    error). Unknown transaction ids return `404`.


    ## Simple HTTP Interface (legacy)


    `GET /v1/sendtts` is the drop-in replacement for the previous Txt2Voice

    service; existing integrations only change the host name. New

    integrations should use `POST /v1/messages` instead; this interface exists

    for migration and will eventually be deprecated.


    Query parameters (all values URI-encoded; names are case sensitive):


    | Parameter | Required | Description |

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

    | `User` | yes | Account username. |

    | `Pass` | yes | Account password. |

    | `To` | yes | Recipient MSISDN. International format with `+` (e.g.
    `+46701234567`); `00`-prefixed international and `0`-prefixed Swedish
    national numbers are also accepted. |

    | `Msg` | yes | The message text. May contain `<break time='100ms'/>` pause
    markup and `<prosody>` tags from the previous service; they are honored.
    Malformed XML is rejected with `400`. |

    | `Lang` | no | Language code, one of: `SE, NO, DK, FI, EN, DE, FR, ES, IT,
    NL, PT, BR, PL, RU, GR`. Default `EN`. |

    | `From` | no | Caller ID (same rules as the REST `sender`: registered E.164
    number). Defaults to the account's configured sender. |

    | `ExternalReferenceId` | no | Client reference echoed in delivery reports.
    |


    Success response (HTTP 202):


    ```

    request=true&TransactionId=fa7a2ada-d3d4-4c93-a125-fec9ecd84f92

    ```


    Failure response (HTTP 200 with `request=false`, or HTTP 400 for

    malformed parameters):


    ```

    request=false&error=Unsupported language: XX

    ```


    Examples:


    ```

    GET
    /txt2voice/v1/sendtts?User=myuser&Pass=mypassword&To=%2B46701234567&Msg=Hello+world

    GET
    /txt2voice/v1/sendtts?User=myuser&Pass=mypassword&To=%2B46701234567&Lang=SE&Msg=Din+kod+%C3%A4r+4+7+2+9

    ```


    The second example sends "Din kod är" followed by the digits 4 7 2 9.
    Writing digits with spaces between them makes the voice read them one by
    one.


    ## Delivery Reports


    Reports are delivered via HTTP GET to the account's configured DLR URL

    with query parameters `TransactionId`, `Result`, `StatusCode`,

    `ProgressTime` (listened seconds), `Sequence` (always `end`, the final

    report), and `ExternalReferenceId`. The format is identical to the previous

    Txt2Voice service:


    ```

    Result=1&StatusCode=1&Sequence=end&TransactionId=fa7a2ada-d3d4-4c93-a125-fec9ecd84f92&ProgressTime=24

    ```


    | Result | StatusCode | Meaning |

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

    | 1 | 1 | Message delivered. |

    | 2 | 1 | Message could not be delivered (no answer after all attempts,
    busy, rejected). |

    | 2 | 5 | Internal error. |


    If the DLR URL is temporarily unreachable, delivery is retried for a limited

    period; if it stays unreachable, delivery is suspended and the reports are
    no

    longer sent (you can still query each transaction's outcome with

    `GET /v1/messages/{transaction_id}`). Keep your DLR endpoint reachable and

    returning a 2xx status. For an unanswered call the report arrives after the
    last

    call reattempt (up to ~5 minutes after submission).
  version: 1.0.0
  x-api-id: txt2voice
  x-api-base-url: https://api.spirius.com/txt2voice
  contact:
    name: Spirius Support
    email: support@spirius.com
  license:
    name: Proprietary
paths:
  /health:
    get:
      tags:
        - Health
      summary: Health Check
      operationId: health_check_health_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /v1/messages:
    post:
      tags:
        - Send Message (REST)
      summary: Send Message
      operationId: send_message_v1_messages_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageRequest'
        required: true
      responses:
        '202':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - HTTPBasic: []
  /v1/messages/{transaction_id}:
    get:
      tags:
        - Message Status
      summary: Get message status
      description: >-
        Returns the current state of a transaction: `accepted` (in progress,
        including automatic reattempts), `completed` (delivered) or `failed`.
        Unknown transaction ids return 404.
      operationId: get_message_status_v1_messages__transaction_id__get
      security:
        - HTTPBasic: []
      parameters:
        - name: transaction_id
          in: path
          required: true
          schema:
            type: string
            title: Transaction Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageStatusResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/sendtts:
    get:
      tags:
        - Send Message (Simple HTTP)
      summary: Send a voice message (legacy interface)
      description: >-
        Drop-in replacement for the previous Txt2Voice service — existing
        integrations only change the host name. Responds with
        `request=true&TransactionId=...` on success and
        `request=false&error=...` on failure. New integrations should use `POST
        /messages`.
      operationId: send_message_simple_http_v1_sendtts_get
      parameters:
        - name: User
          in: query
          required: true
          schema:
            type: string
            description: Account username.
            title: User
          description: Account username.
        - name: Pass
          in: query
          required: true
          schema:
            type: string
            description: Account password.
            title: Pass
          description: Account password.
        - name: To
          in: query
          required: true
          schema:
            type: string
            description: >-
              Recipient MSISDN. International format with `+` (e.g.
              +46701234567); `00`-prefixed international and `0`-prefixed
              Swedish national numbers are also accepted.
            title: To
          description: >-
            Recipient MSISDN. International format with `+` (e.g. +46701234567);
            `00`-prefixed international and `0`-prefixed Swedish national
            numbers are also accepted.
        - name: Msg
          in: query
          required: true
          schema:
            type: string
            description: >-
              The message text. `<break time='100ms'/>` pause markup and
              `<prosody>` tags from the previous service are honored; malformed
              XML is rejected with 400.
            title: Msg
          description: >-
            The message text. `<break time='100ms'/>` pause markup and
            `<prosody>` tags from the previous service are honored; malformed
            XML is rejected with 400.
        - name: Lang
          in: query
          required: false
          schema:
            type: string
            description: >-
              Language code: SE, NO, DK, FI, EN, DE, FR, ES, IT, NL, PT, BR, PL,
              RU, GR. Default EN.
            default: EN
            title: Lang
          description: >-
            Language code: SE, NO, DK, FI, EN, DE, FR, ES, IT, NL, PT, BR, PL,
            RU, GR. Default EN.
        - name: From
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              Caller ID — a registered E.164 number. Defaults to the account's
              configured sender.
            title: From
          description: >-
            Caller ID — a registered E.164 number. Defaults to the account's
            configured sender.
        - name: ExternalReferenceId
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: Client reference echoed in delivery reports.
            title: Externalreferenceid
          description: Client reference echoed in delivery reports.
      responses:
        '202':
          description: Successful Response
          content:
            text/plain:
              schema:
                type: string
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
      type: object
    HealthResponse:
      properties:
        status:
          type: string
          default: ok
      type: object
    MessageRequest:
      properties:
        to:
          type: string
          description: Destination phone number in strict E.164 format, e.g. +46701234567.
        text:
          type: string
          maxLength: 5000
          minLength: 1
          description: >-
            The text to speak.


            **Numbers and codes:** digits separated by spaces are read out one
            by one. `4 7 1 1` is spoken as "four, seven, one, one", while `4711`
            is spoken as "four thousand seven hundred eleven". No pause or delay
            markup is needed between digits.


            SSML is not supported; use `instructions` to control delivery style.
        language:
          type: string
          enum:
            - sv
            - 'no'
            - da
            - fi
            - en-GB
            - en-US
            - de
            - fr
            - es
            - it
            - nl
            - pt-PT
            - pt-BR
            - pl
            - ru
            - ja
            - zh
            - ko
            - ar
            - tr
            - cs
            - ro
            - hu
            - el
            - bg
            - hr
            - sk
            - sl
            - et
            - lv
            - lt
            - uk
            - he
            - th
            - vi
            - id
            - ms
            - hi
            - bn
            - ta
            - te
            - mr
            - gu
            - af
            - ca
          description: >-
            Spoken language as a BCP 47 tag (`sv`, `da`, `de`, `en-US`,
            `pt-BR`). 43 languages are supported through 45 tags: English
            (`en-US`, `en-GB`) and Portuguese (`pt-PT`, `pt-BR`) require the
            regional variant; all other languages use the bare tag.
            Case-insensitive. Defaults to `en-GB`. Text written in another
            language is automatically translated to this language before it is
            spoken; translation can be imperfect, so for exact content such as
            PIN codes send the text already in this language. See the Supported
            Languages table in the API overview for the full list of codes and
            names.
          default: en-GB
        instructions:
          anyOf:
            - type: string
              maxLength: 500
            - type: 'null'
          description: >-
            Optional natural-language delivery instructions for the speech
            engine, describing *how* the text should be spoken. Instructions
            shape delivery only and are not read aloud; put the words to be
            spoken in `text`. For the most reliable results, write instructions
            in English or in the message's spoken language.


            Examples:

            - "Read with a warm, welcoming tone."

            - "Speak slowly and clearly, as if dictating to someone taking
            notes."

            - "Sound cheerful and energetic."

            - "Use a calm, reassuring voice."

            - "Whisper."


            This replaces SSML/prosody markup: instead of `<prosody
            rate="slow">`, write "Read slowly."
        gender:
          anyOf:
            - type: string
              enum:
                - male
                - female
              description: Voice gender selectable on the REST API (`/v1/messages`).
            - type: 'null'
          description: >-
            Voice for the spoken message: `male` or `female`. When omitted, a
            male voice is used.
        sender:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Caller ID shown to the recipient, in strict E.164 format (e.g.
            +46701234567). The number must be registered with Spirius before
            use. Requests with an unregistered sender are rejected immediately
            (422) with instructions. When omitted, the account's configured
            default sender is used, or the service default caller ID if the
            account has none. Alphanumeric SMS sender IDs cannot be used for
            voice calls.
        play_count:
          anyOf:
            - type: integer
              maximum: 10
              minimum: 1
            - type: 'null'
          description: >-
            How many times the message audio is played during the call. Defaults
            to 3 times.
        external_reference_id:
          anyOf:
            - type: string
            - type: 'null'
          description: Client-supplied reference echoed back in delivery reports.
        machine_detection:
          type: boolean
          description: >-
            Optional answering-machine detection. Enabled, the call waits out an
            answering-machine greeting so the message lands intact on the
            recording. This adds a few seconds of delay after a human answers
            and requires the callee to speak when answering. Disabled (the
            default), playback starts immediately when the call is answered.
          default: false
      type: object
      required:
        - to
        - text
      examples:
        - instructions: Read slowly and clearly, with a calm tone.
          language: en-US
          text: Your verification code is 4 7 1 1.
          to: '+46701234567'
    MessageResponse:
      properties:
        transaction_id:
          type: string
        status:
          type: string
          default: accepted
      type: object
      required:
        - transaction_id
    MessageStatusResponse:
      properties:
        transaction_id:
          type: string
        status:
          type: string
          description: 'Transaction outcome: accepted (in flight), completed, or failed.'
        created:
          type: string
          format: date-time
          description: When the message was accepted (UTC, ISO 8601).
        duration_seconds:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Connected call duration in seconds. Only set when status is
            completed.
        failure_code:
          anyOf:
            - type: integer
            - type: 'null'
          description: Failure status code. Only set when status is failed.
      type: object
      required:
        - transaction_id
        - status
        - created
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
        msg:
          type: string
        type:
          type: string
        input: {}
        ctx:
          type: object
      type: object
      required:
        - loc
        - msg
        - type
  securitySchemes:
    HTTPBasic:
      type: http
      scheme: basic
servers:
  - url: https://api.spirius.com/txt2voice
    description: Txt2Voice API server
