openapi: 3.0.2
info:
  version: 1.6.0
  title: SMS API
  description: >
    # Introduction

    Spirius SMS Gateway REST interface is the recommended way to add messaging
    capabilities to your applications.

    It is easy to use but still offer the same performance and reliability as
    its predecessors.

    The API allows you to send and receive SMS messages as well as tracking
    delivery of sent messages.


    ## Base URL

    The following URLs can be used by to access the REST API


    | Site            | URL                          | Description

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

    | Karlskrona      | https://rest1.spirius.com/v1 | Application Load Balancer
    with automatic failover.

    | Stockholm       | https://rest2.spirius.com/v1 | Application Load Balancer
    with automatic failover.

    | Balanced        | https://rest.spirius.com/v1  | DNS Load Balancing
    between Sites listed above.



    ## Authentication

    This API supports two different authentication schemes:


    * [HTTP Basic
    Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)

    * [HMAC-SHA256](https://en.wikipedia.org/wiki/HMAC)


    HTTPS encryption is used to protect communications between your application
    and

    Spirius SMS Gateway.


    In addition to the authentication schemes, Spirius SMS Gateway allows users
    to lock usage to a single IPv4

    address, or to a list of IPv4 addresses. This setting is found in the
    Account Settings section of the

    [customer portal](https://portal.spirius.com)


    > **Note** that HMAC-SHA256 based authentication is the most secure method
    offered.

    > No password or token is sent over the wire and replay of requests is
    mitigated thanks to a timestamp

    > being part of the signature.



    ### Basic Authentication


    In basic HTTP authentication, a request contains a header field in the form
    of `Authorization: Basic <credentials>`,

    where credentials is the Base64 encoding of username and password joined by
    a single colon.


    For example, given username `bob` and password `secret`, the string to
    Base64 encode is `bob:secret` and

    the final result for the header is `Authorization: Basic Ym9iOnNlY3JldAo=`.


    > Basic authentication is simple to implement and most HTTP clients have
    support for this feature.

    > Since Spirius SMS gateway uses HTTPS, the credentials are always
    encrypted.


    ### HMAC-SHA256


    Spirius SMS Gateway supports the use of HMAC (hash-based message
    authentication code) to verify that a request is

    coming from an expected source and has not been tampered with. The client
    generates a unique HMAC representing

    its request to the server and sends that HMAC to the server, along with a
    timestamp and all the arguments

    and values it was going to send anyway. The SMS Gateway API gets the request
    and re-generates its own unique

    HMAC based on the submitted values using the same methods the client used.
    The SMS Gateway API will then compare

    the two HMACs. If they are equal, then the server trusts the client, and
    processes the request.


    Following code snippet demonstrates how to generate HMAC signatures:


    ```python

    import base64

    import json

    import hmac

    import time

    import hashlib


    # Settings

    encoding = "utf-8"

    auth_version = "SpiriusSmsV1"

    api_key = "963e3454-1626-11ec-b249-67af30055a2c".encode(encoding)

    path = "/sms/mt/send"


    # Create message with parameters

    data = {
      "message": "Hello world!",
      "to": "+46123456790",
      "from": "SPIRIUS",
    }

    request_body = json.dumps(data)


    # Build HMAC

    http_verb = "POST"

    unix_timestamp = int(time.time())

    body_hash = hashlib.sha1(request_body.encode(encoding))

    msg_to_sign = "\n".join([
      auth_version,
      str(unix_timestamp),
      http_verb,
      path,
      body_hash.hexdigest()
    ]).encode(encoding)


    # Create signature

    digest = hmac.new(
      key=api_key,
      msg=msg_to_sign,
      digestmod=hashlib.sha256
    ).digest()


    signature = base64.b64encode(digest).decode(encoding)

    print(signature)

    ```


    Complete code examples for sending requests with HMAC signatures are
    available on

    [GitHub](https://github.com/spiriusab/rest-api-examples).


    ### Further settings when deploying HMAC-SHA based authentication

    Things to consider

    - Disable Basic authentication in the [customer
    portal](https://portal.spirius.com) by checking "Enforce HMAC signature"

    - Make sure your machine uses
    [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) (or SNTP) to
    prevent messages to be rejected because of clock drift.


    ## Rate Limit

    Clients are setup with a rate limit that defines the maximum number of HTTP
    requests that can be sent per second.

    When the rate limit is exceeded requests will be rejected with a HTTP status
    429 “Too many requests” response.

    Once the client drops below the limit, SMS Gateway will again start
    processing requests.


    A single HTTP request listing 50 recipients will count as 1 request for HTTP
    protocol rate limiting purposes,

    but the 50 messages will be subject to throttling and only sent out with the
    maximum speed (in SMS/second) defined

    by the rate limit of the client.


    The same kind of throttling also applies to concatenated messages. A single
    message with a text long enough to

    require three concatenated SMS to carry the message contents, will be
    counted as three SMS the current second.



    ## Conventions

    [JavaScript Object Notation](https://en.wikipedia.org/wiki/JSON) is used for
    HTTP bodies in both requests and replies.


    Receiver phone numbers (MSISDN) must be specified in international
    [E.164](https://en.wikipedia.org/wiki/E.164) format with leading + (e.g.
    +46123456789)


    ## Webhooks

    If configured, SMS Gateway will initiate HTTP requests to the client
    whenever a new MO message or status

    notification arrives. Webhook URLs are preset in the system when

    the account is setup and can be changed at any time using the [customer
    portal](https://portal.spirius.com).


    > **Note** that the webhook URL configured in the account settings may be
    overridden using the `dlrCallbackUrl`

    > parameter of the [Send SMS](./#operation/sendSms) endpoint.


    Webhooks support HTTPS encryption and Basic Authentication. This allows you
    to password protect the URLs on your

    web server so that only you and Spirius SMS Gateway can access them. You may
    provide a username and password using

    the following URL format.

    ```http

    https://username:password@www.myserver.com/sms/dlr

    ```


    Webook requests must be responded to using a HTTP status 200 or 202 to
    acknowledge reception of the call

    and thereby stop retransmissions. Retransmissions will initially occur once
    every minute. After 15 minutes the

    retry frequency will start to drop and after 1 week the attempts will cease.
  x-api-id: sms
  x-api-base-url: https://rest.spirius.com/v1
servers:
  - url: https://rest.spirius.com/v1
    description: SMS API server
tags:
  - name: Mobile Terminated SMS
    description: >
      These endpoints are related to sending SMS and tracking delivery of sent
      messages.


      A single SMS can contain maximum 160 characters, or maximum 70 characters
      if it contains emojis or any other character that is not part of the
      [GSM-7](https://en.wikipedia.org/wiki/GSM_03.38) character set. When
      sending longer messages, up to 10 SMS will be concatenated to carry the
      message content. The maximum limit is imposed by mobile networks. Requests
      to send larger messages than what can fit within 10 concatenated SMS will
      be rejected with HTTP status 400.

      > **Note** A concatenated SMS can contain less characters than said above
      (usually 153) because some space is needed for the concatenation header.
      To find out how many SMS that will be used, the `dryRun` parameter can be
      used and then check `numberOfSms` in the response.


      The encoding used in the SMS (i.e.
      [GSM-7](https://en.wikipedia.org/wiki/GSM_03.38) or
      [UCS-2](https://en.wikipedia.org/wiki/Universal_Coded_Character_Set#Encoding_forms))
      is automatically chosen by the SMS Gateway depending on the message
      content.


      The number of SMS needed to carry the message payload is returned from the
      [Send SMS](./#operation/sendSms) endpoint on all HTTP `202 Accepted`
      responses (this is true for dry-runs as well).
  - name: Receive SMS
    description: >
      Using SMS Gateway, it is possible to receive MO (Mobile Originated)
      messages sent to virtual numbers.

      To obtain MO messages, the user has two options: using a
      [webhook](./#tag/Webhook) or repeatedly calling

      [List all messages](./#tag/Receive-SMS/paths/~1sms~1mo~1/get) followed by

      [Pop specific
      message](./#tag/Receive-SMS/paths/~1sms~1mo~1{transactionId}/delete) when
      a new message arrives.

      Webhook is the recommended way to receive MO messages.


      When a message has been delivered to the client, it will be deleted from
      SMS Gateway.
paths:
  /sms/mt/send:
    post:
      tags:
        - Mobile Terminated SMS
      summary: Send SMS
      description: >
        Send an SMS to one or more recipients.


        When an SMS is accepted for delivery, HTTP `202 Accepted` will be
        returned. Other possible status codes

        are specified below.


        To obtain the outcome of a MT message the user has

        two options, calling [Get SMS Status](./#operation/getStatus) or using
        the [SMS status](./#operation/webhookDlr) webhook.


        To request a SMS status webhook event to be generated when an MT message
        has reached its final state,

        set the (`deliveryReport=true`) parameter in the request.
      operationId: sendSms
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageBody'
      responses:
        '202':
          description: Accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSmsOkResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BadRequestResponse'
              examples:
                MissingToParam:
                  $ref: '#/components/examples/missingToParam'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '402':
          description: Payment required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentRequiredResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ForbiddenResponse'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendSmsRateLimitExceededResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
      x-codeSamples:
        - lang: curl
          label: curl
          source: |
            curl -XPOST https://rest.spirius.com/v1/sms/mt/send \
              -d '{"message": "Hello world!", "to": "+46123456790", "from": "SPIRIUS"}' \
              -H 'Content-Type: application/json' \
              -u MyUsername:MyPassword
        - lang: python
          label: Python 3
          source: |
            # requests is a great HTTP client for Python.
            # See https://docs.python-requests.org/
            import requests
            from requests.auth import HTTPBasicAuth

            endpoint = "https://rest.spirius.com/v1/sms/mt/send"
            data = {
              "message": "Hello world!",
              "to": "+46123456790",
              "from": "SPIRIUS",
            }
            credentials = ("MyUsername", "MyPassword")

            response = requests.post(
              endpoint,
              json=data,
              auth=HTTPBasicAuth(*credentials),
            )

            print(response.text)
        - lang: javascript
          label: Node.js
          source: |
            // axios is a promise-based HTTP client for JS.
            // See https://axios-http.com/
            const axios = require('axios').default;

            const endpoint = 'https://rest.spirius.com/v1/sms/mt/send';
            const data = {
              message: "Hello world!",
              to: "+46123456790",
              from: "SPIRIUS",
            };
            const config = {
              auth: {
                username: 'MyUsername',
                password: 'MyPassword',
              }
            };

            axios.post(endpoint, data, config)
              .then((response) => {
                console.log(response.data);
              })
              .catch((error) => {
                console.error(error.response.data);
              });
        - lang: php
          label: PHP
          source: |
            // This example uses the HTTP client Guzzle.
            // See https://docs.guzzlephp.org/

            define('ENDPOINT', 'https://rest.spirius.com/v1/sms/mt/send')
            define('USERNAME', 'MyUsername')
            define('PASSWORD', 'MyPassword')

            $client = new GuzzleHttp\Client();
            $response = $client->request(
              'POST',
              ENDPOINT,
              [
                'auth' => [USERNAME, PASSWORD],
                'json' => [
                  'message' => 'Hello world!',
                  'to' => '+46123456790',
                  'from' => 'SPIRIUS',
                ]
              ]
            );

            echo $response->getStatusCode();
            echo $response->getBody();
        - lang: java
          label: Java
          source: >
            // This example uses the builtin HttpClient that was introduced in
            Java 11.

            // It also uses the jackson library to serialize data to JSON.

            // See https://github.com/FasterXML/jackson

            import com.fasterxml.jackson.databind.ObjectMapper;


            import java.io.IOException;

            import java.net.URI;

            import java.net.http.HttpClient;

            import java.net.http.HttpRequest;

            import java.net.http.HttpResponse;

            import java.util.Base64;

            import java.util.Map;


            public class SmsClient {
                private static final URI ENDPOINT = URI.create("https://rest.spirius.com/v1/sms/mt/send");
                private static final String USERNAME = "MyUsername";
                private static final String PASSWORD = "MyPassword";

                public static void main(String[] args) throws IOException, InterruptedException {
                    var data = Map.of(
                            "message", "Hello world!",
                            "to", "+46123456790",
                            "from", "SPIRIUS"
                    );
                    var requestBody = new ObjectMapper()
                            .writerWithDefaultPrettyPrinter()
                            .writeValueAsString(data);

                    var basicAuth = "Basic " + Base64
                            .getEncoder()
                            .encodeToString((USERNAME + ":" + PASSWORD).getBytes());

                    var request = HttpRequest.newBuilder()
                            .uri(SmsClient.ENDPOINT)
                            .header("Authorization", basicAuth)
                            .header("Content-Type", "application/json")
                            .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                            .build();

                    var response = HttpClient
                            .newHttpClient()
                            .send(request, HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: go
          label: Go
          source: |
            package main

            import (
                "bytes"
                "encoding/json"
                "fmt"
                "io"
                "io/ioutil"
                "log"
                "net/http"
            )

            func main() {
                const URL = "https://rest.spirius.com/v1/sms/mt/send"
                const USERNAME = "MyUsername"
                const PASSWORD = "MyPassword"

                // Create request body
                var data, err = json.Marshal(map[string]string{
                    "message": "Hello world!",
                    "to": "+46123456790",
                    "from": "SPIRIUS",
                })
                if err != nil {
                    log.Fatalf("Could not serialize data: %v", err)
                }
                requestBody := bytes.NewBuffer(data)

                // Create request with basic auth
                request, err := http.NewRequest("POST", URL, requestBody)
                if err != nil {
                    log.Fatalf("Could not create request: %v", err)
                }
                request.SetBasicAuth(USERNAME, PASSWORD)
                request.Header.Set("Content-Type", "application/json")


                // Send request
                response, err := http.DefaultClient.Do(request)
                if err != nil {
                    log.Fatalf("Could not send request: %v", err)
                }

                // Defer closing of response body
                defer func(Body io.ReadCloser) {
                    err := Body.Close()
                    if err != nil {
                        log.Printf("Could not close response: %v\n", err)
                    }
                }(response.Body)

                // Read response body into a string
                responseBody, err := ioutil.ReadAll(response.Body)
                if err != nil {
                    log.Fatalf("Could not read response body: %v", err)
                }
                stringBody := string(responseBody)

                fmt.Println(response.StatusCode)
                fmt.Println(stringBody)
            }
  /sms/mt/status/{transactionId}:
    get:
      tags:
        - Mobile Terminated SMS
      summary: Get SMS status
      description: >
        Get the status of a sent message, identified by transaction ID.


        When calling this endpoint, be sure to look at both the HTTP response
        code and the actual contents

        of the response to determine the status of the message. Take a look at
        the response samples

        for possible combinations.


        You need to send SMS status requests to the same site as was used to
        send the message. If the message was sent from the

        Stockholm site you need to check SMS status from the Stockholm site.
        Other sites will respond with HTTP status

        `404 Not found`. The recommended way is to get SMS status using the
        [webhook](./#operation/webhookDlr).


        SMS status will not be available forever via this API. Make sure to read
        it within 30 days from when the SMS was sent.
      operationId: getStatus
      parameters:
        - in: path
          name: transactionId
          schema:
            type: string
            pattern: >-
              ^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$
          required: true
          description: Transaction ID of message to get status for. Case insensitive.
          example: 0e5d76c2-ca5a-4019-b5e2-08ab66cc101d
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
              examples:
                Delivered:
                  $ref: '#/components/examples/statusDelivered'
                Not delivered:
                  $ref: '#/components/examples/statusNotDelivered'
                Pending:
                  $ref: '#/components/examples/statusPending'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionNotFoundResponse'
              examples:
                Transaction not found:
                  $ref: '#/components/examples/statusNotFound'
        '410':
          description: Expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionExpiredResponse'
              examples:
                Message expired:
                  $ref: '#/components/examples/statusExpired'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionNotRoutedResponse'
              examples:
                Routing error:
                  $ref: '#/components/examples/statusRoutingError'
  /sms/mt/status/external/{externalMessageId}:
    get:
      tags:
        - Mobile Terminated SMS
      summary: Get SMS status external ID
      description: >
        Get the status of a sent message, identified by client provided id.


        When using this endpoint, be sure to look at both the HTTP response code
        and the actual contents

        of the response to determine the status of the message. Take a look at
        the response samples

        for possible combinations.


        You need to send SMS status requests to the same site as was used to
        send the message. If the message was sent from the

        Stockholm site you need to check SMS status from the Stockholm site.
        Other sites will respond with HTTP status

        `404 Not found`. The recommended way is to get SMS status using the
        [webhook](./#operation/webhookDlr).


        SMS status will not be available forever via this API. Make sure to read
        it within 30 days from when the SMS was sent.
      operationId: getStatusExternal
      parameters:
        - in: path
          name: externalMessageId
          schema:
            type: string
            pattern: ^[.*]{0,64}$
          required: true
          description: Unique client provided ID of message to get status for.
          example: AA-B-12345678901234567890
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
              examples:
                Delivered:
                  $ref: '#/components/examples/statusDeliveredExternalId'
                Not delivered:
                  $ref: '#/components/examples/statusNotDeliveredExternalId'
                Pending:
                  $ref: '#/components/examples/statusPendingExternalId'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '404':
          description: Transaction not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionNotFoundResponse'
              examples:
                Transaction not found:
                  $ref: '#/components/examples/statusNotFoundExternalId'
        '410':
          description: Expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionExpiredResponse'
              examples:
                Message expired:
                  $ref: '#/components/examples/statusExpiredExternalId'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
        '503':
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionNotRoutedResponse'
              examples:
                Routing error:
                  $ref: '#/components/examples/statusRoutingErrorExternalId'
  /sms/mo/:
    get:
      tags:
        - Receive SMS
      summary: List all messages
      description: >
        Get a list of all waiting messages. The list includes transaction IDs
        that can then be used to

        pop specific messages using the

        [pop message
        operation](./#tag/Receive-SMS/paths/~1sms~1mo~1{transactionId}/delete).
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageList'
              examples:
                Two messages:
                  $ref: '#/components/examples/messageListTwoMessages'
                No messages:
                  $ref: '#/components/examples/messageListNoMessages'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
  /sms/mo/{transactionId}:
    get:
      tags:
        - Receive SMS
      summary: Get specific message
      description: >
        Get a specific mobile originated message, identified by transaction ID.


        This endpoint is identical to [Pop specific
        message](./#tag/Receive-SMS/paths/~1sms~1mo~1{transactionId}/delete)

        except that this endpoint *does not* delete the message.


        > **Note** that this endpoint is intended for testing purposes only. In
        production environments customers are

        > expected to pop messages from the SMS Gateway.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessage'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessageNotFound'
              examples:
                Not found:
                  $ref: '#/components/examples/moMessageNotFound'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
    delete:
      tags:
        - Receive SMS
      summary: Pop specific message
      description: >
        Get and delete a specific mobile originated message, identified by
        transaction ID.


        > **Note** that once this operation is performed, the message is
        permanently deleted and cannot be retrieved

        > again, so make sure to save it to avoid it being lost. If you want to
        only get the message without deleting

        > it, use [Get specific
        message](./#tag/Receive-SMS/paths/~1sms~1mo~1{transactionId}/get).
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessage'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessageNotFound'
              examples:
                Not found:
                  $ref: '#/components/examples/moMessageNotFound'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
  /sms/mo/next:
    delete:
      tags:
        - Receive SMS
      summary: Pop next message
      description: >
        Get and delete the oldest available mobile originated message.


        > **Note** that once this operation is performed, the message is
        permanently deleted and cannot be retrieved

        > again, so make sure to save it to avoid it being lost.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessage'
        '401':
          description: Not authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotAuthenticatedResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MobileOriginatedMessageNoMore'
              examples:
                No more messages:
                  $ref: '#/components/examples/moMessageNoMessages'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerErrorResponse'
components:
  schemas:
    SendMessageBody:
      title: Send message body
      required:
        - message
        - to
        - from
      type: object
      properties:
        message:
          pattern: ^.*$
          type: string
          example: Hello world!
          description: >-
            The actual message. If message type is binary this parameter should
            be given in hexadecimal form.
        to:
          oneOf:
            - type: string
              pattern: ^\+[1-9]\d{1,14}$
              example: '+46123456790'
            - type: array
              example:
                - '+46123456790'
                - '+46123456791'
              items:
                type: string
          description: >
            The [E.164](https://en.wikipedia.org/wiki/E.164) formatted phone
            number(s) of the receiver(s).

            Numbers must start with a `+` character followed by at least 5, and
            at most 15, digits.


            This value can either be a string or an array of strings.


            A maximum of **50** recipients in a single request is supported.
          example: '+46123456790'
        from:
          pattern: ^.*$
          type: string
          description: >
            The displayed sender. Can either be an
            [E.164](https://en.wikipedia.org/wiki/E.164) formatted phone number,

            or an alphanumeric string of max 11 characters.


            If a default value for `from` is configured in the [customer
            portal](https://portal.spirius.com),

            this parameter can be omitted.
          example: SPIRIUS
        fromType:
          pattern: ^international|national|alphanumeric|short$
          type: string
          enum:
            - international
            - national
            - alphanumeric
            - short
          default: international
          example: alphanumeric
          description: >
            Determines the format of the `from` parameter.


            If the `from` parameter contains any non-digits (other than the
            leading `+`), the `fromType` is

            automatically set to `alphanumeric` if nothing else is provided. An
            alphanumeric fromType might not be

            possible to respond to even if the from value looks like a phone
            number. If the from value contains

            non-digits it is definitely not possible to use as a respond-to
            address.
        sendAt:
          default: null
          pattern: >-
            ^(\d{4})-([0]\d|1[0-2])-([0-2]\d|3[01])
            ([0-1]\d|2[0-4]):([0-5][0-9]):([0-5][0-9])$
          type: string
          format: date-time
          example: '2021-12-10 12:30:00'
          description: >
            A date and time, specifying when to first attempt delivery of the
            SMS. When this parameter is omitted,

            the first delivery attempt is immediate.


            The timestamp must be provided in the format `YYYY-MM-DD HH:mm:ss`
            (e.g. `2021-01-01 15:25:01`).


            The timezone for this parameters is specified in the
            `sendAtTimeZone` parameter.
        sendAtTimeZone:
          type: string
          default: Europe/Stockholm
          example: Europe/Stockholm
          description: >
            Specified timezone for the `sendAt` property.


            The format should be the long version as specified in

            [tz database](https://en.wikipedia.org/wiki/Tz_database) (i.e.
            `Europe/Stockholm`).


            The default value is `Europe/Stockholm`.
        validityPeriod:
          type: integer
          default: 4320
          example: 180
          maximum: 20160
          minimum: 1
          description: >
            A validity period specified in minutes, specifying when to expire
            the SMS if it has not yet been delivered.

            Calculated from the first delivery attempt specified by the `sendAt`
            parameter.


            > **Note** Some mobile network operators enforce a shorter maximum
            validity period (e.g. 2880 minutes) that will

            > override this parameter.


            The max value is `20160` minutes.
        deliveryReport:
          type: boolean
          default: false
          description: >-
            Determines whether delivery report webhook is requested. The
            delivery report URL should be set on the account in the account
            settings section in the [customer
            portal](https://portal.spirius.com).
          example: true
        dlrCallbackUrl:
          type: string
          default: null
          format: url
          description: >
            A temporary webhook URL to send delivery report to over `HTTP POST`
            valid for this message only.

            This parameter overrides the delivery report URL saved on the
            account in the account settings section in

            the [customer portal](https://portal.spirius.com).


            The format of the webhook is documented under the [Webhook
            section](./#tag/Webhook). If the `dlrCallbackUrl`

            is not a valid URL the request will be rejected.


            > **Note** If the `deliveryReport` parameter is not explicitly set,
            its value will change to true when

            > `dlrCallbackUrl` is set.
          example: https://www.mycompany.com/on_dlr
        externalMessageId:
          pattern: ^[.*]{0,64}$
          type: string
          default: null
          maxLength: 64
          description: >
            Optional client provided message id that uniquely identifies the
            message.


            Max 64 basic letters.
          example: AA-B-12345678901234567890
        clientReference:
          pattern: ^[.*]{0,64}$
          type: string
          default: null
          maxLength: 64
          description: >
            A reference that can be used to tag each SMS with a reference string
            of your choice, e.g. a project name.

            You can then get a monthly report with the number of SMS sent per
            project.


            Max 64 basic letters.
          example: Christmas sale
        type:
          type: string
          default: text
          pattern: ^text|flash|binary$
          enum:
            - text
            - flash
            - binary
          example: text
          description: >
            Determines the type of the `message` parameter.


            > **Note** Some mobile network operators do only support ascii
            characters in flash messages.

            >

            > Binary SMS do not support concatenation and cannot carry more than
            140 bytes.
        encoding:
          type: string
          default: auto
          pattern: ^auto|GSM|UCS$
          enum:
            - auto
            - GSM
            - UCS
          example: UCS
          description: >
            When this parameter is omitted or set to `auto`, the API will
            autodetect if the message content can be

            encoded using GSM-7 or if the message contains characters that
            require UCS-2 (such as e.g. emojis).

            The most efficient encoding will be used.


            This parameter can be set to `GSM`or `UCS` to bypass the
            auto-detection algorithm and enforce a specific

            encoding.


            Not applicable for message `type` binary.
        dryRun:
          type: boolean
          default: false
          example: false
          description: >
            If true, enables a dry run, which will validate the message, process
            it and return a result, but no

            message will actually be sent, and the account *will not* be
            charged.


            This is handy for verifying integrations towards the API and testing
            this endpoint without sending anything

            or having any expenses incurred.
    MessageList:
      type: object
      properties:
        detail:
          description: >-
            Human readable information about the number of messages waiting to
            be popped.
          type: string
        transactionIds:
          description: >-
            An array of transaction IDs that can be used with the pop message
            operation.
          type: array
          items:
            type: string
    MobileOriginatedMessage:
      type: object
      properties:
        detail:
          type: string
          description: Message received
          example: Message received
        from:
          type: string
          description: >-
            The displayed sender. Can either be an
            [E.164](https://en.wikipedia.org/wiki/E.164) formatted phone number,
            or an alphanumeric string.
          example: SPIRIUS
        fromType:
          type: string
          enum:
            - international
            - national
            - alphanumeric
            - short
          example: alphanumeric
        message:
          type: string
          description: The message content
          example: Hello world!
        type:
          type: string
          enum:
            - text
            - flash
            - binary
          example: text
          description: |
            Determines the type of the `message` parameter.
        result:
          type: integer
          enum:
            - 1
            - 2
            - 3
            - 4
          description: |
            A code describing the result of this operation.

            The meaning of available options are:
            * `1` No error
            * `2` Unknown transaction ID
            * `3` No more messsages are waiting
            * `4` An internal error occurred
          example: 1
        timestamp:
          type: string
          format: date-time
          description: >
            A date and time, specifying when the message was received by
            Spirius.


            The timestamp is of format `YYYY-MM-DD HH:mm:ss` (e.g. `2021-01-01
            15:25:01`).


            The timezone for this parameters is specified with the `timezone`
            parameter.
          example: '2021-10-09 15:35:21'
        timezone:
          type: string
          description: >
            Specified timezone for the `timestamp` property.


            The format is a timezone abbreviation

            [timezone
            abbrevation](https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations)
            (i.e. `CEST`).
          example: CEST
        to:
          type: string
          description: The recipient of the message
          example: '+4671234567'
        toType:
          type: string
          enum:
            - international
            - national
            - alphanumeric
            - short
          example: international
        transactionId:
          type: string
          description: |
            The transaction ID for this message.
          example: 28fc2e4a-abc2-4942-9eec-a61f9dcfb54d
    MobileOriginatedMessageNotFound:
      type: object
      properties:
        detail:
          type: string
          description: Not found
          example: Not found
        result:
          type: integer
          description: 2
          example: 2
    MobileOriginatedMessageNoMore:
      type: object
      properties:
        detail:
          type: string
          description: No more messages
          example: No more messages
        result:
          type: integer
          description: 3
          example: 3
    BadRequestResponse:
      type: object
      properties:
        detail:
          type: string
          description: A description of why the request could not be accepted
          example: Missing 'from' parameter
    InternalServerErrorResponse:
      type: object
      properties:
        detail:
          type: string
          description: Internal error
          example: Internal error
    NotAuthenticatedResponse:
      type: object
      properties:
        detail:
          type: string
          description: >
            Not authenticated

            > **Note** Invaid credentials. No further information will be given
            away by the API.
          example: Not authenticated
    ForbiddenResponse:
      type: object
      properties:
        detail:
          type: string
          description: Source IP not whitelisted
          example: Source IP not whitelisted
    PaymentRequiredResponse:
      type: object
      properties:
        detail:
          type: string
          description: Account is disabled
          example: Account is disabled
    SendSmsOkResponse:
      type: object
      properties:
        detail:
          type: string
          description: Accepted for delivery
          example: Accepted for delivery
        numberOfSms:
          type: integer
          description: The number of SMS the message will be split into before being sent
          example: 1
        transactionId:
          description: The transaction ID for this message
          oneOf:
            - type: string
            - type: array
              items:
                type: string
                description: The transaction ID for this message
          example: 28fc2e4a-abc2-4942-9eec-a61f9dcfb54d
        remainingRequestQuota:
          type: integer
          description: >
            The remaining http request quota. Defined as the API client's rate
            limit minus the number of http requests

            accepted during the preceding 1000 milliseconds when this response
            was generated.
          example: 25
    SendSmsRateLimitExceededResponse:
      type: object
      properties:
        detail:
          type: string
          description: Rate limit exceeded
          example: Rate limit exceeded
    StatusResponse:
      type: object
      properties:
        result:
          type: integer
          description: |
            The result of this transaction.

            Available options are:

            * `1` - delivered
            * `2` - failed to deliver
            * `32` - the message is still pending
        statusCode:
          type: integer
          description: >
            The status code is used to differentiate between different kind of
            failures. The value

            is only meaningful when the result code is 2 (`result=2`).


            Available values are:


            * `1` - The message could not be delivered

            * `2` - The message could not be routed

            * `3` - Validity period expired

            * `4` - Unknown transaction id

            * `5` - Internal error
        detail:
          type: string
          description: >-
            A humanly readable description of the result + status code
            combination for this transaction
        transactionId:
          type: string
          description: The transaction ID of the message that this status belongs to
        externalMessageId:
          type: string
          description: >-
            If an externalMessageId was given when the SMS was sent, it will be
            included in the webhook
        timestamp:
          type: string
          description: The time when delivery report was received from the mobile network
        timezone:
          type: string
          description: >-
            The timezone information for above timestamp. Expected to be CET or
            CEST.
    TransactionNotFoundResponse:
      type: object
      properties:
        result:
          type: integer
          description: 2
        statusCode:
          type: integer
          description: 4
        detail:
          type: string
          description: Not found
        transactionId:
          type: string
          description: A transaction ID
        timestamp:
          type: string
        timezone:
          type: string
    TransactionExpiredResponse:
      type: object
      properties:
        result:
          type: integer
          description: 2
        statusCode:
          type: integer
          description: 3
        detail:
          type: string
          description: >-
            The message could not be delivered before the validity period
            expired
        transactionId:
          type: string
          description: A transaction ID
        timestamp:
          type: string
          description: The time when expiration report was received from the mobile network
        timezone:
          type: string
          description: >-
            The timezone information for above timestamp. Expected to be CET or
            CEST.
    TransactionNotRoutedResponse:
      type: object
      properties:
        result:
          type: integer
          description: 2
        statusCode:
          type: integer
          description: 2
        detail:
          type: string
          description: The message could not be routed
        transactionId:
          type: string
          description: A transaction ID
        timestamp:
          type: string
          description: The time for when this non-final status was generated
        timezone:
          type: string
          description: >-
            The timezone information for above timestamp. Expected to be CET or
            CEST.
  examples:
    moMessageNotFound:
      value:
        detail: Not found
        result: 2
    moMessageNoMessages:
      value:
        detail: No more messages
    moMessageInternalError:
      value:
        detail: An internal error occured
    missingToParam:
      value:
        detail: 'Missing mandatory parameter: ''to'''
    statusDelivered:
      value:
        result: 1
        statusCode: 1
        detail: The message has been successfully delivered
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        timestamp: '2021-09-23 14:51:00'
        timezone: CEST
    statusDeliveredExternalId:
      value:
        result: 1
        statusCode: 1
        detail: The message has been successfully delivered
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        externalMessageId: AA-B-12345678901234567890
        timestamp: '2021-09-23 14:51:00'
        timezone: CEST
    statusNotDelivered:
      value:
        result: 2
        statusCode: 1
        detail: The message could not be delivered
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        timestamp: '2021-09-23 14:51:00'
        timezone: CEST
    statusNotDeliveredExternalId:
      value:
        result: 2
        statusCode: 1
        detail: The message could not be delivered
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        externalMessageId: AA-B-12345678901234567890
        timestamp: '2021-09-23 14:51:00'
        timezone: CEST
    statusRoutingError:
      value:
        result: 2
        statusCode: 2
        detail: The message could not be routed
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        timestamp: '2021-09-23 14:51:02'
        timezone: CEST
    statusRoutingErrorExternalId:
      value:
        result: 2
        statusCode: 2
        detail: The message could not be routed
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        externalMessageId: AA-B-12345678901234567890
        timestamp: '2021-09-23 14:51:02'
        timezone: CEST
    statusExpired:
      value:
        result: 2
        statusCode: 3
        detail: The message could not be delivered before the validity period expired
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        timestamp: '2021-09-23 14:51:01'
        timezone: CEST
    statusExpiredExternalId:
      value:
        result: 2
        statusCode: 3
        detail: The message could not be delivered before the validity period expired
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        externalMessageId: AA-B-12345678901234567890
        timestamp: '2021-09-23 14:51:01'
        timezone: CEST
    statusNotFound:
      value:
        result: 2
        statusCode: 4
        detail: Not found
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        timestamp: ''
        timezone: ''
    statusNotFoundExternalId:
      value:
        result: 2
        statusCode: 4
        detail: Not found
        transactionId: ''
        externalMessageId: AA-B-12345678901234567890
        timestamp: ''
        timezone: ''
    statusPending:
      value:
        result: 32
        statusCode: 0
        detail: The message is pending
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
    statusPendingExternalId:
      value:
        result: 32
        statusCode: 0
        detail: The message is pending
        transactionId: 6b252bed-c967-446b-bfba-e71427c188a9
        externalMessageId: AA-B-12345678901234567890
    messageListTwoMessages:
      value:
        detail: 2 messages
        transactionIds:
          - aa313168-c9f9-4b25-aa14-38dadfb8ef8e
          - 56ef83b9-a535-46ec-8511-bcb30370f51b
    messageListNoMessages:
      value:
        detail: 0 messages
        transactionIds: []
x-webhooks:
  webhookDlr:
    post:
      summary: SMS status
      description: >
        MT-SMS status notification webhooks are sent in JSON format using `HTTP
        POST` and JSON.


        The URL is configured on the Account setting page in the [customer
        portal](https://portal.spirius.com).
      operationId: webhookDlr
      tags:
        - Mobile Terminated SMS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusResponse'
            examples:
              Delivered:
                $ref: '#/components/examples/statusDelivered'
              Not delivered:
                $ref: '#/components/examples/statusNotDelivered'
      responses:
        '200':
          description: >-
            Return a 200 status to indicate that the data was received
            successfully
  webhookMoSms:
    post:
      summary: Mobile Originated SMS
      description: >
        MO Messages are sent in JSON format using `HTTP POST` with Content-Type:
        application/json.


        On the Account settings page in the [customer
        portal](https://portal.spirius.com), make sure to select the

        Virtual Number Protocol option `HTTP REST` to get the format described
        in this documentation. For new accounts

        this option should already be preset.


        On the same page you also configure the Virtual Number URL, i.e. the
        destination your MO messages will be sent to.
      operationId: webhookMoSms
      tags:
        - Receive SMS
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MobileOriginatedMessage'
      responses:
        '200':
          description: >-
            Return a 200 status to indicate that the data was received
            successfully
