{
  "openapi": "3.1.0",
  "info": {
    "title": "SENDLY SMS API",
    "version": "1.2",
    "description": "SENDLY SMS API działa na zasadzie przesyłania komunikatów za pomocą protokołu HTTP. Do obsługi wiadomości SMS dostępne są dwa wzajemnie wykluczające się tryby: SMS API oraz 3CX API. Token możliwy jest do wygenerowania i pobrania z panelu klienta (app.sendly.link); alternatywnie przez biuro obsługi klienta. Wiadomości wychodzące kodowane są w UCS-2 i dzielone co 60 znaków; opcjonalnie GSM7 (pojedyncza wiadomość, maks. 160 znaków) – do włączenia w panelu klienta lub przez BOK.",
    "contact": {
      "name": "SENDLY",
      "url": "https://sendly.link/pl/kontakt/",
      "email": "sendly@sendly.link"
    }
  },
  "externalDocs": {
    "description": "Pełna specyfikacja SENDLY SMS API v1.2",
    "url": "https://sendly.link/pl/dokumentacja/specyfikacja/"
  },
  "servers": [
    {
      "url": "https://api.sendly.link"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/sms": {
      "post": {
        "operationId": "sendSms",
        "summary": "Wysyłka wiadomości SMS",
        "description": "Domyślny tryb działania API, przeznaczony do wykorzystania z aplikacjami obsługującymi REST API.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "body"
                ],
                "properties": {
                  "from": {
                    "type": "string",
                    "pattern": "^[0-9]{9,11}$",
                    "description": "Numer wirtualnego numeru komórkowego w SENDLY (pole opcjonalne)."
                  },
                  "to": {
                    "type": "string",
                    "pattern": "^[0-9]{9,11}$",
                    "description": "Docelowy numer komórkowy."
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Treść wiadomości."
                  }
                }
              },
              "example": {
                "from": "48732129000",
                "to": "48732129001",
                "body": "Test SENDLY"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Wiadomość została przyjęta do wysyłki.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageAccepted"
                },
                "example": {
                  "message_id": "a906cff7719bd889"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/AuthError"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/sms-multi": {
      "post": {
        "operationId": "sendSmsMulti",
        "summary": "Wysyłka wiadomości na wiele numerów",
        "description": "Wysyłka tej samej wiadomości na wiele numerów. Aktywacja tej funkcji dla tokena dostępowego następuje wyłącznie poprzez kontakt z biurem obsługi klienta. Walidacja numerów na zasadzie „wszystko albo nic” – każdy numer musi być poprawnym polskim numerem komórkowym, numery muszą być unikalne; inaczej żądanie zostaje odrzucone w całości.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "body"
                ],
                "properties": {
                  "from": {
                    "type": "string",
                    "pattern": "^[0-9]{9,11}$",
                    "description": "Numer wirtualnego numeru komórkowego w SENDLY (pole opcjonalne)."
                  },
                  "to": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "uniqueItems": true,
                    "items": {
                      "type": "string",
                      "pattern": "^[0-9]{9,11}$"
                    },
                    "description": "Docelowe numery komórkowe."
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Treść wiadomości."
                  }
                }
              },
              "example": {
                "from": "48732129000",
                "to": [
                  "48732129001",
                  "48732129002"
                ],
                "body": "Test SENDLY"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Wiadomość została przyjęta do wysyłki.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagesAccepted"
                },
                "example": {
                  "message_ids": [
                    {
                      "number": "48732129001",
                      "message_id": "a906cff7719bd889"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/AuthError"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/tcx": {
      "post": {
        "operationId": "tcxSms",
        "summary": "3CX SMS API",
        "description": "Tryb 3CX SMS API: wysyłka i odbiór wiadomości na podstawie aktualnej specyfikacji centrali 3CX Phone System. Aktywacja: w panelu klienta obie opcje (SMS API oraz 3CX SMS API); w sekcji SMS w 3CX podaje się token z panelu oraz ten adres URL. Format żądań/odpowiedzi zgodny ze specyfikacją 3CX.",
        "responses": {
          "200": {
            "description": "Zgodnie ze specyfikacją centrali 3CX Phone System."
          }
        }
      }
    }
  },
  "webhooks": {
    "inboundMessage": {
      "post": {
        "summary": "Odbiór wiadomości (webhook)",
        "description": "Wiadomości przychodzące wysyłane są na podany w panelu klienta webhook. Jedna próba dostarczenia; bez śledzenia przekierowań i bez autoryzacji. Żądanie pochodzi z aktualnego adresu IP domeny api.sendly.link.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "const": "MESSAGE",
                    "description": "Typ wiadomości – dla przychodzącej „MESSAGE\"."
                  },
                  "from": {
                    "type": "string",
                    "description": "Prezentacja numeru źródłowego; przy nadpisanym numerze – nadpis."
                  },
                  "to": {
                    "type": "string",
                    "description": "Docelowy numer telefonu."
                  },
                  "body": {
                    "type": "string",
                    "description": "Treść wiadomości."
                  }
                }
              },
              "example": {
                "type": "MESSAGE",
                "from": "48732129000",
                "to": "48732129001",
                "body": "Test sms"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Potwierdzenie odbioru przez webhook."
          }
        }
      }
    },
    "deliveryNotification": {
      "post": {
        "summary": "Potwierdzenie dostarczenia wiadomości (webhook)",
        "description": "Po aktywacji opcji (panel klienta lub BOK) żądanie wysyłane jest na podany adres webhooka. Jedna próba dostarczenia; bez śledzenia przekierowań i bez autoryzacji. Żądanie pochodzi z aktualnego adresu IP domeny api.sendly.link.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "const": "NOTIFICATION",
                    "description": "Typ wiadomości – dla statusu „NOTIFICATION\"."
                  },
                  "message_id": {
                    "type": "string",
                    "description": "Identyfikator wiadomości zwrócony po przyjęciu do wysyłki."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "DELIVERED",
                      "ERROR"
                    ],
                    "description": "DELIVERED – dostarczono; ERROR – błąd."
                  }
                }
              },
              "example": {
                "type": "NOTIFICATION",
                "message_id": "a906cff7719bd889",
                "status": "DELIVERED"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Potwierdzenie odbioru przez webhook."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer token wygenerowany w panelu klienta (app.sendly.link) lub uzyskany przez biuro obsługi klienta."
      }
    },
    "schemas": {
      "MessageAccepted": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "description": "Unikalny identyfikator wiadomości (16 znaków hex)."
          }
        }
      },
      "MessagesAccepted": {
        "type": "object",
        "properties": {
          "message_ids": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "number": {
                  "type": "string",
                  "description": "Jeden z podanych numerów docelowych."
                },
                "message_id": {
                  "type": "string",
                  "description": "Identyfikator wiadomości przyjętej do wysyłki."
                }
              }
            }
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "object",
            "properties": {
              "token": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Błędy związane z tokenem dostępowym."
              },
              "from": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Błędy związane z numerem źródłowym."
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Błędy związane z numerem docelowym."
              },
              "body": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Błędy związane z ciałem wiadomości."
              }
            }
          }
        }
      }
    },
    "responses": {
      "AuthError": {
        "description": "Problem z autoryzacją.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            },
            "example": {
              "errors": {
                "token": [
                  "Invalid token"
                ]
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Problem z walidacją danych.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Przekroczono limit żądań."
      }
    }
  }
}