openapi: 3.1.0
info:
  title: "Rayo SMS API"
  version: "1.0.0"
  description: |
    # Introduction

    Welcome to the [Rayo SMS](https://www.rayo.gr) API documentation.

    You need to study this guide if you want to integrate your application with Web-to-SMS Functionality via [Rayo SMS](https://www.rayo.gr).

    # SDKs Documentation

    If you prefer using a ready-made SDK instead of calling the HTTP endpoints directly, check out the [Rayo SMS SDKs documentation](https://sms-sdks-docs.rayo.gr).

    # Base Endpoint

    The endpoint that you should be sending your API requests to is:

    ```text
    https://sms-api.rayo.gr/v1/
    ```

    # Authentication

    The endpoint, although public, it requires that you send your own, private, API Key as part of the HTTP `Authorization` header.

    You can get and manage your API keys from the `Actions -> Settings` page inside the [Rayo SMS](https://sms.rayo.gr) Web application.

    Note that you can have many API keys active at the same time. This will be useful when you decide to decommission an old API key
    and replace it with a new one. You might want to keep both active at the same time for a short period of time until all of your
    systems that integrate with Rayo SMS finally use the new API key. Or you can use two different API keys for
    different purposes. It's up to you.

    In the following paragraphs, where we describe the different API features, we will give examples on how you would pass the Authorization
    as part of the HTTP Request Headers, but until then, here is an example of a valid header:

    `Authorization: Bearer 1212845e-3517-436f-8f31-86cfb51f6251`

    Where `1212845e-3517-436f-8f31-86cfb51f6251`, is valid API key. Replace this, with your own API key.

    **Important**: Treat the API keys as confidential as you keep your passwords. Do not store them in version control systems and do not reveal to
    public. Always use end-to-end encryption (`HTTPS`) between the client application and the Rayo SMS endpoints when using the API.

    # Errors

    A lot of things might go wrong with your HTTP Requests. Rayo SMS API returns
    [Standard HTTP Response Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to
    indicate the success or failure of the request.

    When the HTTP Response code is in the range `400-599`, this means that something has gone wrong.

    The following are common error cases. You can see the HTTP Response Status Code and possible
    causes of error:

    | Status Code | Explanation |
    | --- | --- |
    | `401` | You have provided a non-existing or inactive API key |
    | `422` | When the data in the request are invalid. For example, for an SMS request, the `fromLabel` does not belong to you. |
    | `500-599` | Indicates an error that took place on the server side. Rayo SMS developers are automatically notified for such errors and will immediately try to remedy this situation. If your problem persists, use the contact form on [Rayo SMS Web site](https://www.rayo.gr) to report the problem. |

    In case you get an error in the range `500-599`, we are suggesting that you retry your
    request. Maybe with an increasing wait time in-between the retries. You can retry up to a certain
    amount of times before you give up.

    **Structure of JSON Object in Response Body when Error:**

    ```json
    {
      "result": {
        "username": "...",
        "resultCode": "0",
        "resultDescription": "ERROR: ...",
        "clientRequestId": "...",
        "smsServerRequestId": null
      },
      "error": "..."
    }
    ```

    The body of the HTTP Response body, might give you more information about what was the problem. If it is
    not empty, it will contain a JSON object. Take a look at the `Structure of JSON Object in Response Body when Error`.

    # More API Endpoints on the Way

    We are in the process of developing more API endpoints that will allow you to:

    * Manage Contacts, and
    * Manage groups
  contact:
    url: "https://www.rayo.gr"
servers:
  - url: "https://sms-api.rayo.gr"
    description: Production
tags:
  - name: SMS
    description: Send SMS messages immediately or schedule them for future delivery.
  - name: Account
    description: Retrieve account information.
  - name: Delivery status
    description: Retrieve SMS delivery information.
  - name: Schedules
    description: Manage scheduled SMS deliveries.
security:
  - ApiKeyAuth: []
paths:
  /v1/send_sms:
    post:
      tags:
        - SMS
      summary: Sending an SMS
      description: |
        Sending an SMS requires you to send a `POST` request to the path `/send_sms`.

        The HTTP Request should have the following headers, as a minimum requirement:

        ### Request Headers

        | Name | Value |
        | --- | --- |
        | Content-Type | application/json |
        | Accept | application/json |
        | Authorization | Bearer [your API key here] |

        For the `Authorization` header above, replace the `[your API key here]` with your
        own API key.

        The payload of the request should be sent in the body of the request as a string.
        The string should be a string encoding of a JSON object. The JSON object should
        have the following properties:

        ### JSON Object Properties

        | Name | Type | Description | Example |
        | --- | --- | --- | --- |
        | `toMobileNumber` | string | The mobile number this sms will be sent to. It can be a comma-separated or semi-colon separated list of mobile numbers. | `306972669766` or `306972669766;306972669764` |
        | `toGroup` | string | The name of the Group this sms will be sent to. Group is one of your groups registered in the application. You can specify multiple groups separated using `;` | `High Valued Customers;Promotion Customers` |
        | `fromLabel` | string | The sender identifier. One of the sender ids that you have registered in the application. | `BOXNOW` |
        | `smsBody` | string | The SMS content. | `Hello! You have just won 5 Euro Gift Card!` |
        | `clientRequestId` | string | The client request id that will be attached to the sms request. This is optional. It can be used for searching/reporting. It's a business-level value that makes sense to your own business. The application does not process it at all, other than for indexing the message. | `9543212` |
        | `callbackWhenProcessedFlag` | boolean | Default `false`. When `true`, SMS server will call the callback api endpoint to report back the status of delivery. You must have set up a callback api in the application settings. | `true` |
        | `simulateSendFlag` | boolean | Default `false`. When `true`, the SMS will not actually be delivered. This can be used to test the integration without consuming credits. | `false` |
        | `scheduledAt` | string | You can schedule a delivery to take place in the future. **Important:** It should not be earlier than 5 minutes in the future. It needs to be ISO8601 with Time Zone representation of a date and time | `"2024-12-13T11:34:29+02:00"` |

        ### HTTP Response

        #### Success

        On successful sending of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
        a body that will be a JSON object encoded as string. The JSON object will have the following structure:

        | Name | Type | Description |
        | --- | --- | --- |
        | `result` | object | JSON root level object. All the other properties, below, they are properties of this object. |
        | `result.username` | string | The username identified by the API key used in the incoming SMS request. |
        | `result.resultCode` | string | It will have the value "1" on success. Different from "1" on failure. |
        | `result.resultDescription` | string | It will have the value "SUCCESS" on success. Different from "SUCCESS" on failure. |
        | `result.clientRequestId` | string | It will be either `null` or it will have the value of the `clientRequestId` sent in the incoming SMS request. |
        | `result.smsServerRequestId` | string | A unique identifier from the SMS server side. This uniquely identifies your SMS request. It will value `null` if you schedule the SMS in the future. |
        | `result.smsRequestScheduleId` | string | A unique identifier of the schedule created when you schedule an SMS to be delivered in the future. It will have value `null` if you don't schedule. |

        #### Error

        With regards to error handling, read the corresponding **Errors** section above.
      operationId: sendSms
      x-codeSamples:
        - lang: Shell
          label: cURL
          source: |
            curl -v -X POST 'https://sms-api.rayo.gr/v1/send_sms' \
              -H 'Content-Type: application/json' \
              -H 'Accept: application/json' \
              -H 'Authorization: Bearer 5a2a5816-5f32-4234-a9a1-7d59986c06a5' \
              -d '{
                "toMobileNumber":"6972669766",
                "fromLabel":"306972669766",
                "smsBody":"test message"
              }'
        - lang: Ruby
          label: Ruby
          source: |
            # This Ruby example uses Rest Client library (https://github.com/rest-client/rest-client).
            # You can use any other Ruby library that can construct standard HTTPS requests.
            #
            require 'rest-client'

            api_key = '5a2a5816-5f32-4234-a9a1-7d59986c06a5'
            sms_body = 'test message'

            url = 'https://sms-api.rayo.gr/v1/send_sms'
            payload = {
              toMobileNumber: '6972669766',
              fromLabel: '306972669766',
              smsBody: sms_body
            }.to_json
            headers = {
              'Content-Type' => 'application/json',
              'Accept' => 'application/json',
              'Authorization' => "Bearer #{api_key}"
            }

            response = RestClient.post url, payload, headers
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendSmsRequest"
            examples:
              mobileNumber:
                summary: Send to one mobile number
                value:
                  toMobileNumber: "6972669766"
                  fromLabel: "306972669766"
                  smsBody: test message
      responses:
        "200":
          description: |
            On successful sending of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
            a body that will be a JSON object encoded as string. The JSON object will have the following structure:

            | Name | Type | Description |
            | --- | --- | --- |
            | `result` | object | JSON root level object. All the other properties, below, they are properties of this object. |
            | `result.username` | string | The username identified by the API key used in the incoming SMS request. |
            | `result.resultCode` | string | It will have the value "1" on success. Different from "1" on failure. |
            | `result.resultDescription` | string | It will have the value "SUCCESS" on success. Different from "SUCCESS" on failure. |
            | `result.clientRequestId` | string | It will be either `null` or it will have the value of the `clientRequestId` sent in the incoming SMS request. |
            | `result.smsServerRequestId` | string | A unique identifier from the SMS server side. This uniquely identifies your SMS request. It will value `null` if you schedule the SMS in the future. |
            | `result.smsRequestScheduleId` | string | A unique identifier of the schedule created when you schedule an SMS to be delivered in the future. It will have value `null` if you don't schedule. |
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendSmsSuccessResponse"
              example:
                result:
                  username: user1
                  resultCode: "1"
                  resultDescription: SUCCESS
                  clientRequestId: "932831"
                  smsServerRequestId: cc479845-0f15-4ae7-b645-6ef6489868c1
                  smsRequestScheduleId: e0b107da-669a-40f5-b4d0-24fc70409307
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"
        "5XX":
          $ref: "#/components/responses/ServerError"
  /v1/balance_in_credits:
    get:
      tags:
        - Account
      summary: Get Balance in Credits
      description: |
        This is the API that will return your current balance in credits. It is a `GET` request to the path
        `/balance_in_credits`.

        The HTTP Request should have the following headers, as a minimum requirement:

        ### Request Headers

        | Name | Value |
        | --- | --- |
        | Accept | application/json |
        | Authorization | Bearer [your API key here] |

        For the `Authorization` header above, replace the `[your API key here]` with your
        own API key.

        ### HTTP Response

        #### Success

        On successful return of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
        a body that will be a JSON object encoded as string. The JSON object will have the following structure:

        | Name | Type | Description |
        | --- | --- | --- |
        | `request_id` | string | Unique identifier of your request. |
        | `balance` | object | Object with details about your balance. |
        | `balance.credits` | number | The credits remaining in your balance. |

        #### Error

        With regards to error handling, read the corresponding **Errors** section above.
      operationId: getBalanceInCredits
      x-codeSamples:
        - lang: Shell
          label: cURL
          source: |
            curl -v -X GET 'https://sms-api.rayo.gr/v1/balance_in_credits' \
              -H 'Accept: application/json' \
              -H 'Authorization: Bearer 5a2a5816-5f32-4234-a9a1-7d59986c06a5'
        - lang: Ruby
          label: Ruby
          source: |
            # This Ruby example uses Rest Client library (https://github.com/rest-client/rest-client).
            # You can use any other Ruby library that can construct standard HTTPS requests.
            #
            require 'rest-client'

            api_key = '5a2a5816-5f32-4234-a9a1-7d59986c06a5'

            url = 'https://sms-api.rayo.gr/v1/balance_in_credits'
            headers = {
              'Accept' => 'application/json',
              'Authorization' => "Bearer #{api_key}"
            }

            response = RestClient.get url, headers
      responses:
        "200":
          description: |
            On successful return of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
            a body that will be a JSON object encoded as string. The JSON object will have the following structure:

            | Name | Type | Description |
            | --- | --- | --- |
            | `request_id` | string | Unique identifier of your request. |
            | `balance` | object | Object with details about your balance. |
            | `balance.credits` | number | The credits remaining in your balance. |
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BalanceResponse"
              example:
                request_id: 7d6939ec-7213-4bf9-9b41-2902b54cfa71
                balance:
                  credits: 4240.0
        "401":
          $ref: "#/components/responses/Unauthorized"
        "5XX":
          $ref: "#/components/responses/ServerError"
  /v1/check_status:
    get:
      tags:
        - Delivery status
      summary: Check Status of Sms Request
      description: |
        This is the API that will return back the status of an SMS Request. Note that
        an SMS Request might correspond to multiple outgoing SMS messages. This
        API request will return the status of each one of the outgoing message
        that belongs to the specified SMS request.

        The SMS Request can be located by one, but not both, of the following:

        1. `clientRequestId`. This should be the `clientRequestId` that you specified when
        you sent the request.
        2. `smsServerRequestId`. This should be the `smsServerRequestId` that you got back
        in the response of the send request.

        The HTTP Request should have the following headers, as a minimum requirement:

        ### Request Headers

        | Name | Value |
        | --- | --- |
        | Accept | application/json |
        | Authorization | Bearer [your API key here] |

        For the `Authorization` header above, replace the `[your API key here]` with your
        own API key.

        ### HTTP Response

        #### Success

        On successful return of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
        a body that will be a JSON object encoded as string. The JSON object will have the following structure:

        It will be an `Array` of objects of the following structure.

        > Note that an SMS Request might correspond to multiple outgoing SMS messages. This
        > API request will return the status of each one of the outgoing message
        > that belongs to the specified SMS request.

        | Name | Type | Description |
        | --- | --- | --- |
        | `smsServerRequestId` | string | The SMS Server Request Id of the original SMS Request |
        | `clientRequestId` | string | The Client Request Id that you have assigned to the SMS Request |
        | `internalSmsOutDeliveryId` | string | Unique identifier of the outgoing SMS |
        | `mobileNumber` | string | The destination mobile number for the specific outgoing SMS |
        | `status` | string | The delivery status of the outgoing SMS. See below the table with possible values |

        ##### SMS Delivery Status

        The possible delivery status values are:

        | Status | Explanation |
        | --- | --- |
        | `new` | Initial Status. |
        | `temp_locked` | Internal Status. |
        | `processing_error` | Failed to Process the Request. Message was not sent out. We will not retry to send it out. |
        | `failure` | We tried to send the message out multiple times, but we failed all of them. |
        | `sent` | The message was sent out. |
        | `error` | There is a temporary error. We will try to send the message again. |
        | `delivered` | Message was delivered to the handset. |
        | `not_delivered` | Message was sent out, but it was not delivered to the handset. |
        | `unknown_delivery` | Message was sent out, but we don't know whether it was delivered or not. |

        An outgoing message that leaves our network is on status `sent`.
        If it is delivered to the handset, it will be on status `delivered`.
        If it cannot be delivered by the mobile network, it will be on
        status `not_delivered`. If we don't get a feedback from the
        device whether the message has been delivered or not after a day,
        it will be turned to `unknown_delivery`.

        #### Error

        With regards to error handling, read the corresponding **Errors** section above.

        Note, that if you specify a `clientRequestId` and/or an `smsServerRequestId` that does not match
        any SMS Request in your account, the API request will return HTTP Response Status code `404` and
        the response body will be:

        ```json
        {
          "error": "No record(s) found"
        }
        ```
      operationId: checkSmsRequestStatus
      x-codeSamples:
        - lang: Shell
          label: cURL
          source: |
            # Get the status of an SMS request by Client Request Id (clientRequestId).
            #
            curl -v -X GET 'https://sms-api.rayo.gr/v1/check_status?clientRequestId=<a-client-request-id>' \
              -H 'Accept: application/json' \
              -H 'Authorization: Bearer 5a2a5816-5f32-4234-a9a1-7d59986c06a5'

            # Get the status of an SMS request by SMS Server Request Id (smsServerRequestId).
            #
            curl -v -X GET 'https://sms-api.rayo.gr/v1/check_status?smsServerRequestId=<an-sms-server-request-id>' \
              -H 'Accept: application/json' \
              -H 'Authorization: Bearer 5a2a5816-5f32-4234-a9a1-7d59986c06a5'
        - lang: Ruby
          label: Ruby
          source: |
            # This Ruby example uses Rest Client library (https://github.com/rest-client/rest-client).
            # You can use any other Ruby library that can construct standard HTTPS requests.
            #
            require 'rest-client'

            api_key = '5a2a5816-5f32-4234-a9a1-7d59986c06a5'

            # Get the status of an SMS request by Client Request Id (clientRequestId).
            #
            url = 'https://sms-api.rayo.gr/v1/check_status?clientRequestId=<a-client-request-id>'
            headers = {
              'Accept' => 'application/json',
              'Authorization' => "Bearer #{api_key}"
            }

            response = RestClient.get url, headers

            # Get the status of an SMS request by SMS Server Request Id (smsServerRequestId).
            #
            url = 'https://sms-api.rayo.gr/v1/check_status?smsServerRequestId=<an-sms-server-request-id>'
            headers = {
              'Accept' => 'application/json',
              'Authorization' => "Bearer #{api_key}"
            }

            response = RestClient.get url, headers
      parameters:
        - $ref: "#/components/parameters/ClientRequestId"
        - $ref: "#/components/parameters/SmsServerRequestId"
      responses:
        "200":
          description: |
            On successful return of the HTTP request, you will get back a standard HTTP response code with value `200`. It will have
            a body that will be a JSON object encoded as string. It will be an `Array` of objects of the following structure:

            | Name | Type | Description |
            | --- | --- | --- |
            | `smsServerRequestId` | string | The SMS Server Request Id of the original SMS Request |
            | `clientRequestId` | string | The Client Request Id that you have assigned to the SMS Request |
            | `internalSmsOutDeliveryId` | string | Unique identifier of the outgoing SMS |
            | `mobileNumber` | string | The destination mobile number for the specific outgoing SMS |
            | `status` | string | The delivery status of the outgoing SMS. |
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/SmsDeliveryStatus"
              example:
                - smsServerRequestId: "1396522"
                  clientRequestId: "20240316203045"
                  internalSmsOutDeliveryId: "1065597"
                  mobileNumber: "306972669766"
                  status: delivered
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: |
            If you specify a `clientRequestId` and/or an `smsServerRequestId` that does not match
            any SMS Request in your account, the API request will return HTTP Response Status code `404`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundResponse"
              example:
                error: No record(s) found
        "5XX":
          $ref: "#/components/responses/ServerError"
  /v1/sms_request_schedules/{id}:
    delete:
      tags:
        - Schedules
      summary: Deleting a schedule
      description: |
        You can delete a schedule that you have created. This is only possible as long as the
        schedule has not fired.

        You have to pass the correct authorization headers as described in the Authentication section.
      operationId: deleteSmsRequestSchedule
      x-codeSamples:
        - lang: Shell
          label: cURL
          source: |
            # Delete a schedule using the schedule identifier
            #
            curl -v -X DELETE 'https://sms-api.rayo.gr/v1/sms_request_schedules/e0b107da-669a-40f5-b4d0-24fc70409307' \
              -H 'Accept: application/json' \
              -H 'Authorization: Bearer 5a2a5816-5f32-4234-a9a1-7d59986c06a5'
        - lang: Ruby
          label: Ruby
          source: |
            # This Ruby example uses Rest Client library (https://github.com/rest-client/rest-client).
            # You can use any other Ruby library that can construct standard HTTPS requests.
            #
            require 'rest-client'

            api_key = '5a2a5816-5f32-4234-a9a1-7d59986c06a5'

            # Delete a schedule using the schedule identifier
            #
            url = 'https://sms-api.rayo.gr/v1/sms_request_schedules/<schedule-identifier>'
            headers = {
              'Accept' => 'application/json',
              'Authorization' => "Bearer #{api_key}"
            }

            response = RestClient.delete url, headers
      parameters:
        - name: id
          in: path
          required: true
          description: Identifier returned as `smsRequestScheduleId` when the SMS was scheduled.
          schema:
            type: string
          example: e0b107da-669a-40f5-b4d0-24fc70409307
      responses:
        default:
          description: The public API documentation does not define this response contract.
webhooks:
  deliveryStatusUpdate:
    get:
      summary: Delivery Updates via a Callback Webhook (GET)
      description: |
        You can set up your own endpoint which Rayo SMS API will call every time there is an update
        on the status of an outgoing SMS. However, in order for this to take place, you need to have set the value
        of parameter `callbackWhenProcessedFlag` to `true` when sending an SMS.

        Go to `Actions -> Settings` and locate the section `Callbacks API`. The Callback URL is a service offered by
        Rayo SMS. It gives you the ability to receive delivery reports via a specific URL.
        You specify the URL, as well as whether you want the HTTP request to be a `GET` or a `POST` request.

        Moreover, each call the Rayo SMS API does to your service as a callback, it will have
        a signature that you can check to make sure that the request is legitimate and it is coming from our
        servers. This signature is delivered in the HTTP Request Header `Rayo-SMS-Signature`.

        Whenever we need to update the status of a message, we will be calling your callback endpoint.
        If you prefer the HTTP Verb to be `GET` then the parameters alongside their values are being URL-encoded.

        ### Parameters/Attributes of payload

        The status of the message will be sent back to your endpoint alongside the following parameters:

        | Parameter Name | Type | Description |
        | --- | --- | --- |
        | `smsServerRequestId` | string | This is the unique identifier that matches the initial request to send an SMS. Note that each one request, may be corresponding to more than one SMS sent out. For example, if your request refers to a group with more than one member |
        | `clientRequestId` | string | This is the identifier that you have provided when you sent your initial request. Since this parameter is optional on requests, then it may not be present on the delivery reports. |
        | `internalSmsOutDeliveryId` | string | This is unique identifier for each SMS sent out. It is internally generated. |
        | `senderId` | string | This is the Sender Identifier that has been used to send the SMS out. |
        | `mobileNumber` | string | This is the mobile number that the SMS has been sent out to. |
        | `status` | string | This is the delivery status of the outgoing SMS. Read about the possible values above. |
        | `errorDescription` | string | Usually it contains `no error` but it may contain an error description if the status implies an error. |
        | `requestTimestamp` | string | The timestamp, in iso8601 standard, the SMS request was received by our systems. |
        | `lastStatusUpdatedTimestamp` | string | The timestamp of the last status update. |

        ### Signature of the Callback Request

        1. Take the signature key that you have set up in the Application Settings.
        2. If the callback request is a `POST` request, take the `Content-Type` header value from the request. Otherwise,
        consider the content type equal to a blank string `""`, even if it might have a value.
        3. Take the request path.
        4. Take the payload. For `POST` requests it is the body of the request. For `GET` requests it is the
        query string.
        5. Take the value of the request header `Date`.
        6. Create a comma-separated list of the values for:
           - request method lower cased, i.e. `get` or `post`
           - content type
           - request path
           - payload
           - date header value
        7. Using the string you created in the previous step and the signing secret create a SHA256 hex digest,
        where the signing secret is used as a key and the string as the data.
        8. Compare the resulting SHA256 hex digest to the incoming signature. They should match.
      operationId: receiveDeliveryStatusUpdateByGet
      security: []
      parameters:
        - $ref: "#/components/parameters/CallbackSignature"
        - $ref: "#/components/parameters/CallbackDate"
        - $ref: "#/components/parameters/CallbackSmsServerRequestId"
        - $ref: "#/components/parameters/CallbackClientRequestId"
        - $ref: "#/components/parameters/CallbackInternalSmsOutDeliveryId"
        - $ref: "#/components/parameters/CallbackSenderId"
        - $ref: "#/components/parameters/CallbackMobileNumber"
        - $ref: "#/components/parameters/CallbackStatus"
        - $ref: "#/components/parameters/CallbackErrorDescription"
        - $ref: "#/components/parameters/CallbackRequestTimestamp"
        - $ref: "#/components/parameters/CallbackLastStatusUpdatedTimestamp"
      responses:
        default:
          description: Response returned by the customer callback endpoint.
    post:
      summary: Delivery Updates via a Callback Webhook (POST)
      description: |
        You can set up your own endpoint which Rayo SMS API will call every time there is an update
        on the status of an outgoing SMS. However, in order for this to take place, you need to have set the value
        of parameter `callbackWhenProcessedFlag` to `true` when sending an SMS.

        Go to `Actions -> Settings` and locate the section `Callbacks API`. The Callback URL is a service offered by
        Rayo SMS. It gives you the ability to receive delivery reports via a specific URL.
        You specify the URL, as well as whether you want the HTTP request to be a `GET` or a `POST` request.

        Moreover, each call the Rayo SMS API does to your service as a callback, it will have
        a signature that you can check to make sure that the request is legitimate and it is coming from our
        servers. This signature is delivered in the HTTP Request Header `Rayo-SMS-Signature`.

        Whenever we need to update the status of a message, we will be calling your callback endpoint.
        If you specify the HTTP Verb to be `POST` then the parameters will be delivered to you
        in the body of the request encoded as a JSON object.

        ### Parameters/Attributes of payload

        The status of the message will be sent back to your endpoint alongside the following parameters:

        | Parameter Name | Type | Description |
        | --- | --- | --- |
        | `smsServerRequestId` | string | This is the unique identifier that matches the initial request to send an SMS. Note that each one request, may be corresponding to more than one SMS sent out. For example, if your request refers to a group with more than one member |
        | `clientRequestId` | string | This is the identifier that you have provided when you sent your initial request. Since this parameter is optional on requests, then it may not be present on the delivery reports. |
        | `internalSmsOutDeliveryId` | string | This is unique identifier for each SMS sent out. It is internally generated. |
        | `senderId` | string | This is the Sender Identifier that has been used to send the SMS out. |
        | `mobileNumber` | string | This is the mobile number that the SMS has been sent out to. |
        | `status` | string | This is the delivery status of the outgoing SMS. Read about the possible values above. |
        | `errorDescription` | string | Usually it contains `no error` but it may contain an error description if the status implies an error. |
        | `requestTimestamp` | string | The timestamp, in iso8601 standard, the SMS request was received by our systems. |
        | `lastStatusUpdatedTimestamp` | string | The timestamp of the last status update. |

        ### Signature of the Callback Request

        1. Take the signature key that you have set up in the Application Settings.
        2. If the callback request is a `POST` request, take the `Content-Type` header value from the request. Otherwise,
        consider the content type equal to a blank string `""`, even if it might have a value.
        3. Take the request path.
        4. Take the payload. For `POST` requests it is the body of the request. For `GET` requests it is the
        query string.
        5. Take the value of the request header `Date`.
        6. Create a comma-separated list of the values for:
           - request method lower cased, i.e. `get` or `post`
           - content type
           - request path
           - payload
           - date header value
        7. Using the string you created in the previous step and the signing secret create a SHA256 hex digest,
        where the signing secret is used as a key and the string as the data.
        8. Compare the resulting SHA256 hex digest to the incoming signature. They should match.
      operationId: receiveDeliveryStatusUpdateByPost
      security: []
      x-codeSamples:
        - lang: Ruby
          label: Rails signature verification for GET and POST callbacks
          source: |
            require 'openssl'

            class CallbacksController < ApplicationController
              skip_before_action :verify_authenticity_token

              def callback
                incoming_signature = request.headers['Rayo-SMS-Signature']
                signing_secret = ENV.fetch('SIGNATURE_KEY')

                if valid_signature?(incoming_signature, signing_secret)
                  render json: { message: 'Signature valid' }, status: :ok
                else
                  render json: { message: 'Invalid signature' }, status: :unauthorized
                end
              end

              private

              def valid_signature?(incoming_signature, signing_secret)
                request_method = request.method.downcase
                content_type = request.post? ? request.headers['Content-Type'].to_s : ''
                request_path = request.path
                payload = request.post? ? request.body.read : request.query_string
                date_header = request.headers['Date']

                string_to_sign = [
                  request_method,
                  content_type,
                  request_path,
                  payload,
                  date_header
                ].join(',')

                digest = OpenSSL::HMAC.hexdigest(
                  'SHA256',
                  signing_secret,
                  string_to_sign
                )

                ActiveSupport::SecurityUtils.secure_compare(
                  digest,
                  incoming_signature
                )
              end
            end
      parameters:
        - $ref: "#/components/parameters/CallbackSignature"
        - $ref: "#/components/parameters/CallbackDate"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliveryStatusCallback"
      responses:
        default:
          description: Response returned by the customer callback endpoint.
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        An active API key managed in **Actions -> Settings**. Multiple keys can
        remain active during key rotation. Treat API keys as confidential.
  parameters:
    ClientRequestId:
      name: clientRequestId
      in: query
      required: false
      description: |
        Business identifier assigned to the SMS request by the client. Supply
        this parameter or `smsServerRequestId`, but not both.
      schema:
        type: string
      example: "20240316203045"
    SmsServerRequestId:
      name: smsServerRequestId
      in: query
      required: false
      description: |
        Unique SMS request identifier returned by the server. Supply this
        parameter or `clientRequestId`, but not both.
      schema:
        type: string
      example: cc479845-0f15-4ae7-b645-6ef6489868c1
    CallbackSignature:
      name: "Rayo-SMS-Signature"
      in: header
      required: true
      description: "HMAC-SHA256 hex digest used to verify that the callback came from Rayo SMS."
      schema:
        type: string
    CallbackDate:
      name: Date
      in: header
      required: true
      description: Date header included as the final value in the callback signing string.
      schema:
        type: string
    CallbackSmsServerRequestId:
      name: smsServerRequestId
      in: query
      required: true
      description: This is the unique identifier that matches the initial request to send an SMS. Note that each one request, may be corresponding to more than one SMS sent out. For example, if your request refers to a group with more than one member
      schema:
        type: string
    CallbackClientRequestId:
      name: clientRequestId
      in: query
      required: false
      description: This is the identifier that you have provided when you sent your initial request. Since this parameter is optional on requests, then it may not be present on the delivery reports.
      schema:
        type: string
    CallbackInternalSmsOutDeliveryId:
      name: internalSmsOutDeliveryId
      in: query
      required: true
      description: This is unique identifier for each SMS sent out. It is internally generated.
      schema:
        type: string
    CallbackSenderId:
      name: senderId
      in: query
      required: true
      description: This is the Sender Identifier that has been used to send the SMS out.
      schema:
        type: string
    CallbackMobileNumber:
      name: mobileNumber
      in: query
      required: true
      description: This is the mobile number that the SMS has been sent out to.
      schema:
        type: string
    CallbackStatus:
      name: status
      in: query
      required: true
      description: This is the delivery status of the outgoing SMS. Read about the possible values above.
      schema:
        $ref: "#/components/schemas/DeliveryStatus"
    CallbackErrorDescription:
      name: errorDescription
      in: query
      required: true
      description: Usually it contains `no error` but it may contain an error description if the status implies an error.
      schema:
        type: string
    CallbackRequestTimestamp:
      name: requestTimestamp
      in: query
      required: true
      description: The timestamp, in iso8601 standard, the SMS request was received by our systems.
      schema:
        type: string
        format: date-time
    CallbackLastStatusUpdatedTimestamp:
      name: lastStatusUpdatedTimestamp
      in: query
      required: true
      description: The timestamp of the last status update.
      schema:
        type: string
        format: date-time
  responses:
    Unauthorized:
      description: You have provided a non-existing or inactive API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    UnprocessableEntity:
      description: When the data in the request are invalid. For example, for an SMS request, the `fromLabel` does not belong to you.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ServerError:
      description: |
        Indicates an error that took place on the server side. Rayo SMS developers are automatically notified for such errors and will immediately try to remedy this situation. If your problem persists, use the contact form on [Rayo SMS Web site](https://www.rayo.gr) to report the problem.

        In case you get an error in the range `500-599`, we are suggesting that you retry your request. Maybe with an increasing wait time in-between the retries. You can retry up to a certain amount of times before you give up.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
  schemas:
    SendSmsRequest:
      type: object
      description: SMS request payload.
      required:
        - fromLabel
        - smsBody
      properties:
        toMobileNumber:
          type: string
          description: The mobile number this sms will be sent to. It can be a comma-separated or semi-colon separated list of mobile numbers.
          examples:
            - "306972669766"
            - "306972669766;306972669764"
        toGroup:
          type: string
          description: The name of the Group this sms will be sent to. Group is one of your groups registered in the application. You can specify multiple groups separated using `;`
          example: High Valued Customers;Promotion Customers
        fromLabel:
          type: string
          description: The sender identifier. One of the sender ids that you have registered in the application.
          example: BOXNOW
        smsBody:
          type: string
          description: The SMS content.
          example: Hello! You have just won 5 Euro Gift Card!
        clientRequestId:
          type: string
          description: The client request id that will be attached to the sms request. This is optional. It can be used for searching/reporting. It's a business-level value that makes sense to your own business. The application does not process it at all, other than for indexing the message.
          example: "9543212"
        callbackWhenProcessedFlag:
          type: boolean
          default: false
          description: Default `false`. When `true`, SMS server will call the callback api endpoint to report back the status of delivery. You must have set up a callback api in the application settings.
        simulateSendFlag:
          type: boolean
          default: false
          description: Default `false`. When `true`, the SMS will not actually be delivered. This can be used to test the integration without consuming credits.
        scheduledAt:
          type: string
          format: date-time
          description: You can schedule a delivery to take place in the future. **Important:** It should not be earlier than 5 minutes in the future. It needs to be ISO8601 with Time Zone representation of a date and time
          example: "2024-12-13T11:34:29+02:00"
    SendSmsSuccessResponse:
      type: object
      required:
        - result
      properties:
        result:
          type: object
          description: JSON root level object. All the other properties, below, they are properties of this object.
          required:
            - username
            - resultCode
            - resultDescription
            - clientRequestId
            - smsServerRequestId
            - smsRequestScheduleId
          properties:
            username:
              type: string
              description: The username identified by the API key used in the incoming SMS request.
            resultCode:
              type: string
              const: "1"
              description: It will have the value "1" on success. Different from "1" on failure.
            resultDescription:
              type: string
              const: SUCCESS
              description: It will have the value "SUCCESS" on success. Different from "SUCCESS" on failure.
            clientRequestId:
              type:
                - string
                - "null"
              description: It will be either `null` or it will have the value of the `clientRequestId` sent in the incoming SMS request.
            smsServerRequestId:
              type:
                - string
                - "null"
              description: A unique identifier from the SMS server side. This uniquely identifies your SMS request. It will value `null` if you schedule the SMS in the future.
            smsRequestScheduleId:
              type:
                - string
                - "null"
              description: A unique identifier of the schedule created when you schedule an SMS to be delivered in the future. It will have value `null` if you don't schedule.
    BalanceResponse:
      type: object
      required:
        - request_id
        - balance
      properties:
        request_id:
          type: string
          description: Unique identifier of your request.
        balance:
          type: object
          description: Object with details about your balance.
          required:
            - credits
          properties:
            credits:
              type: number
              description: The credits remaining in your balance.
    SmsDeliveryStatus:
      type: object
      required:
        - smsServerRequestId
        - clientRequestId
        - internalSmsOutDeliveryId
        - mobileNumber
        - status
      properties:
        smsServerRequestId:
          type: string
          description: The SMS Server Request Id of the original SMS Request
        clientRequestId:
          type: string
          description: The Client Request Id that you have assigned to the SMS Request
        internalSmsOutDeliveryId:
          type: string
          description: Unique identifier of the outgoing SMS
        mobileNumber:
          type: string
          description: The destination mobile number for the specific outgoing SMS
        status:
          $ref: "#/components/schemas/DeliveryStatus"
    DeliveryStatus:
      type: string
      description: |
        The delivery status of the outgoing SMS:

        - `new`: Initial Status.
        - `temp_locked`: Internal Status.
        - `processing_error`: Failed to Process the Request. Message was not sent out. We will not retry to send it out.
        - `failure`: We tried to send the message out multiple times, but we failed all of them.
        - `sent`: The message was sent out.
        - `error`: There is a temporary error. We will try to send the message again.
        - `delivered`: Message was delivered to the handset.
        - `not_delivered`: Message was sent out, but it was not delivered to the handset.
        - `unknown_delivery`: Message was sent out, but we don't know whether it was delivered or not.
      enum:
        - new
        - temp_locked
        - processing_error
        - failure
        - sent
        - error
        - delivered
        - not_delivered
        - unknown_delivery
    DeliveryStatusCallback:
      type: object
      required:
        - smsServerRequestId
        - internalSmsOutDeliveryId
        - senderId
        - mobileNumber
        - status
        - errorDescription
        - requestTimestamp
        - lastStatusUpdatedTimestamp
      properties:
        smsServerRequestId:
          type: string
          description: This is the unique identifier that matches the initial request to send an SMS. Note that each one request, may be corresponding to more than one SMS sent out. For example, if your request refers to a group with more than one member
        clientRequestId:
          type: string
          description: This is the identifier that you have provided when you sent your initial request. Since this parameter is optional on requests, then it may not be present on the delivery reports.
        internalSmsOutDeliveryId:
          type: string
          description: This is unique identifier for each SMS sent out. It is internally generated.
        senderId:
          type: string
          description: This is the Sender Identifier that has been used to send the SMS out.
        mobileNumber:
          type: string
          description: This is the mobile number that the SMS has been sent out to.
        status:
          $ref: "#/components/schemas/DeliveryStatus"
        errorDescription:
          type: string
          description: Usually it contains `no error` but it may contain an error description if the status implies an error.
        requestTimestamp:
          type: string
          format: date-time
          description: The timestamp, in iso8601 standard, the SMS request was received by our systems.
        lastStatusUpdatedTimestamp:
          type: string
          format: date-time
          description: The timestamp of the last status update.
    ErrorResponse:
      type: object
      description: |
        Error responses can include further details in their body. Fields may
        be null when the request could not be associated with an account or SMS
        request.
      properties:
        result:
          type: object
          properties:
            username:
              type:
                - string
                - "null"
            resultCode:
              type: string
              description: A value other than `1` indicates failure.
            resultDescription:
              type: string
              description: Description of the failure.
            clientRequestId:
              type:
                - string
                - "null"
            smsServerRequestId:
              type:
                - string
                - "null"
        error:
          type: string
    NotFoundResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
