├── package.json ├── .env.example ├── index.js ├── LICENSE ├── .gitignore └── README.md /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sipgateio-sendsms-node", 3 | "version": "1.0.0", 4 | "description": "A demonstration of how to send SMS using the sipgate REST-API.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "sipgate.io Team ", 11 | "repository": "https://github.com/sipgate-io/sipgateio-sendsms-node", 12 | "license": "Unlicense", 13 | "dependencies": { 14 | "axios": "^0.21.2", 15 | "dotenv": "^16.0.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # URL of the API endpoint. You probably don't have to change this. 2 | baseURL = 'https://api.sipgate.com/v2' 3 | 4 | # Your Personal-Access-Token and the ID. 5 | # Open https://app.sipgate.com and create a new token by navigating to "Benutzereinstellungen" -> "Personal-Access-Tokens". 6 | # The token should have the `sessions:sms:write` scope (see https://www.sipgate.io/rest-api/authentication#personalAccessToken for more information). 7 | tokenId = 'YOUR_SIPGATE_TOKEN_ID' 8 | token = 'YOUR_SIPGATE_TOKEN' 9 | 10 | # Choose a phone number of the recipient. 11 | recipient = 'RECIPIENT_PHONE_NUMBER' 12 | 13 | # You can use the "/{userID}/sms" endpoint to list the available SMS IDs for a given user (see https://api.sipgate.com/v2/doc#/sms/getSmsExtensions). 14 | # Alternatively you can use the Node.JS library (see https://github.com/sipgate-io/sipgateio-node-examples#list-common-ids). 15 | smsId = 'YOUR_SIPGATE_SMS_EXTENSION' 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | require('dotenv').config(); 4 | const {baseURL, tokenId, token, recipient, smsId} = process.env; 5 | 6 | const message = 'YOUR_MESSAGE'; 7 | 8 | // Only needed when sending a scheduled sms 9 | 10 | // const timestamp = new Date('YYYY-MM-DD hh:mm:ss'); 11 | // const sendAt = (timestamp.getTime() / 1000).toString(); 12 | 13 | const data = { 14 | smsId, 15 | recipient, 16 | message, 17 | // sendAt, 18 | }; 19 | 20 | const requestOptions = { 21 | method: 'POST', 22 | headers: { 23 | Accept: 'application/json', 24 | 'Content-Type': 'application/json', 25 | }, 26 | auth: { 27 | username: tokenId, 28 | password: token, 29 | }, 30 | data, 31 | }; 32 | 33 | axios(`${baseURL}/sessions/sms`, requestOptions) 34 | .then(response => { 35 | console.log(`Status: ${response.status}`); 36 | console.log(`Body: ${JSON.stringify(response.data)}`); 37 | }) 38 | .catch(error => console.error('Error: ', error.message)); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/vim,git,macos,linux,intellij+all,visualstudiocode,node 3 | # Edit at https://www.gitignore.io/?templates=vim,git,macos,linux,intellij+all,visualstudiocode,node 4 | 5 | ### Git ### 6 | # Created by git for backups. To disable backups in Git: 7 | # $ git config --global mergetool.keepBackup false 8 | *.orig 9 | 10 | # Created by git when using merge tools for conflicts 11 | *.BACKUP.* 12 | *.BASE.* 13 | *.LOCAL.* 14 | *.REMOTE.* 15 | *_BACKUP_*.txt 16 | *_BASE_*.txt 17 | *_LOCAL_*.txt 18 | *_REMOTE_*.txt 19 | 20 | ### Intellij+all ### 21 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 22 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 23 | 24 | # User-specific stuff 25 | .idea/**/workspace.xml 26 | .idea/**/tasks.xml 27 | .idea/**/usage.statistics.xml 28 | .idea/**/dictionaries 29 | .idea/**/shelf 30 | 31 | # Generated files 32 | .idea/**/contentModel.xml 33 | 34 | # Sensitive or high-churn files 35 | .idea/**/dataSources/ 36 | .idea/**/dataSources.ids 37 | .idea/**/dataSources.local.xml 38 | .idea/**/sqlDataSources.xml 39 | .idea/**/dynamic.xml 40 | .idea/**/uiDesigner.xml 41 | .idea/**/dbnavigator.xml 42 | 43 | # Gradle 44 | .idea/**/gradle.xml 45 | .idea/**/libraries 46 | 47 | # Gradle and Maven with auto-import 48 | # When using Gradle or Maven with auto-import, you should exclude module files, 49 | # since they will be recreated, and may cause churn. Uncomment if using 50 | # auto-import. 51 | # .idea/modules.xml 52 | # .idea/*.iml 53 | # .idea/modules 54 | 55 | # CMake 56 | cmake-build-*/ 57 | 58 | # Mongo Explorer plugin 59 | .idea/**/mongoSettings.xml 60 | 61 | # File-based project format 62 | *.iws 63 | 64 | # IntelliJ 65 | out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Cursive Clojure plugin 74 | .idea/replstate.xml 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | # Editor-based Rest Client 83 | .idea/httpRequests 84 | 85 | # Android studio 3.1+ serialized cache file 86 | .idea/caches/build_file_checksums.ser 87 | 88 | ### Intellij+all Patch ### 89 | # Ignores the whole .idea folder and all .iml files 90 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 91 | 92 | .idea/ 93 | 94 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 95 | 96 | *.iml 97 | modules.xml 98 | .idea/misc.xml 99 | *.ipr 100 | 101 | # Sonarlint plugin 102 | .idea/sonarlint 103 | 104 | ### Linux ### 105 | *~ 106 | 107 | # temporary files which can be created if a process still has a handle open of a deleted file 108 | .fuse_hidden* 109 | 110 | # KDE directory preferences 111 | .directory 112 | 113 | # Linux trash folder which might appear on any partition or disk 114 | .Trash-* 115 | 116 | # .nfs files are created when an open file is removed but is still being accessed 117 | .nfs* 118 | 119 | ### macOS ### 120 | # General 121 | .DS_Store 122 | .AppleDouble 123 | .LSOverride 124 | 125 | # Icon must end with two \r 126 | Icon 127 | 128 | # Thumbnails 129 | ._* 130 | 131 | # Files that might appear in the root of a volume 132 | .DocumentRevisions-V100 133 | .fseventsd 134 | .Spotlight-V100 135 | .TemporaryItems 136 | .Trashes 137 | .VolumeIcon.icns 138 | .com.apple.timemachine.donotpresent 139 | 140 | # Directories potentially created on remote AFP share 141 | .AppleDB 142 | .AppleDesktop 143 | Network Trash Folder 144 | Temporary Items 145 | .apdisk 146 | 147 | ### Node ### 148 | # Logs 149 | logs 150 | *.log 151 | npm-debug.log* 152 | yarn-debug.log* 153 | yarn-error.log* 154 | 155 | # Runtime data 156 | pids 157 | *.pid 158 | *.seed 159 | *.pid.lock 160 | 161 | # Directory for instrumented libs generated by jscoverage/JSCover 162 | lib-cov 163 | 164 | # Coverage directory used by tools like istanbul 165 | coverage 166 | 167 | # nyc test coverage 168 | .nyc_output 169 | 170 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 171 | .grunt 172 | 173 | # Bower dependency directory (https://bower.io/) 174 | bower_components 175 | 176 | # node-waf configuration 177 | .lock-wscript 178 | 179 | # Compiled binary addons (https://nodejs.org/api/addons.html) 180 | build/Release 181 | 182 | # Dependency directories 183 | node_modules/ 184 | jspm_packages/ 185 | 186 | # TypeScript v1 declaration files 187 | typings/ 188 | 189 | # Optional npm cache directory 190 | .npm 191 | 192 | # Optional eslint cache 193 | .eslintcache 194 | 195 | # Optional REPL history 196 | .node_repl_history 197 | 198 | # Output of 'npm pack' 199 | *.tgz 200 | 201 | # Yarn Integrity file 202 | .yarn-integrity 203 | 204 | # dotenv environment variables file 205 | .env 206 | .env.test 207 | 208 | # parcel-bundler cache (https://parceljs.org/) 209 | .cache 210 | 211 | # next.js build output 212 | .next 213 | 214 | # nuxt.js build output 215 | .nuxt 216 | 217 | # vuepress build output 218 | .vuepress/dist 219 | 220 | # Serverless directories 221 | .serverless/ 222 | 223 | # FuseBox cache 224 | .fusebox/ 225 | 226 | # DynamoDB Local files 227 | .dynamodb/ 228 | 229 | ### Vim ### 230 | # Swap 231 | [._]*.s[a-v][a-z] 232 | [._]*.sw[a-p] 233 | [._]s[a-rt-v][a-z] 234 | [._]ss[a-gi-z] 235 | [._]sw[a-p] 236 | 237 | # Session 238 | Session.vim 239 | 240 | # Temporary 241 | .netrwhist 242 | # Auto-generated tag files 243 | tags 244 | # Persistent undo 245 | [._]*.un~ 246 | 247 | ### VisualStudioCode ### 248 | .vscode/* 249 | !.vscode/settings.json 250 | !.vscode/tasks.json 251 | !.vscode/launch.json 252 | !.vscode/extensions.json 253 | 254 | ### VisualStudioCode Patch ### 255 | # Ignore all local history of files 256 | .history 257 | 258 | # End of https://www.gitignore.io/api/vim,git,macos,linux,intellij+all,visualstudiocode,node 259 | 260 | .env 261 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sipgate logo 2 | 3 | # sipgate.io Node.js send sms example 4 | 5 | To demonstrate how to send an SMS, we queried the `/sessions/sms` endpoint of the sipgate REST API. 6 | 7 | For further information regarding the sipgate REST API please visit https://api.sipgate.com/v2/doc 8 | 9 | ### Prerequisites 10 | 11 | - Node.js >= 10.15.3 12 | 13 | ### How To Use 14 | 15 | Navigate to the project's root directory. 16 | 17 | Install dependencies: 18 | 19 | ```bash 20 | npm install 21 | ``` 22 | 23 | Create the `.env` file by copying the `.env.example`. Set the values according to the comment above each variable. 24 | 25 | The token should have the `sessions:sms:write` scope. For more information about personal access tokens visit https://www.sipgate.io/rest-api/authentication#personalAccessToken. 26 | 27 | In order to run the code you have to set the following variable in [index.js](./index.js): 28 | 29 | ```javascript 30 | const message = "YOUR_MESSAGE"; 31 | ``` 32 | 33 | The `smsId` uniquely identifies the extension from which you wish to send your message. Further explanation is given in the section [Web SMS Extensions](#web-sms-extensions). 34 | 35 | > **Optional:** 36 | > In order to send a delayed message uncomment the following line and set the desired date and time in the future (up to one month): 37 | > 38 | > ```javascript 39 | > const timestamp = new Date("YYYY-MM-DD hh:mm:ss"); 40 | > const sendAt = (timestamp.getTime() / 1000).toString(); 41 | > ``` 42 | > 43 | > Additionally, in the `data` object uncomment the `sendAt` property. 44 | > 45 | > ```javascript 46 | > const data = { 47 | > smsId, 48 | > recipient, 49 | > message, 50 | > sendAt, 51 | > }; 52 | > ``` 53 | > 54 | > **Note:** The `data` object is written in javascript Object Property Value Shorthand. 55 | > 56 | > **Note:** The `sendAt` property in the `data` object is a [Unix timestamp](https://www.unixtimestamp.com/). 57 | 58 | Run the application: 59 | 60 | ```bash 61 | npm run start 62 | ``` 63 | 64 | ##### How It Works 65 | 66 | The sipgate REST API is available under the following base URL: 67 | 68 | ```javascript 69 | const baseURL = "https://api.sipgate.com/v2"; 70 | ``` 71 | 72 | The API expects request data in JSON format. Thus the `Content-Type` header needs to be set accordingly. 73 | 74 | ```javascript 75 | headers: { 76 | 'Content-Type': 'application/json', 77 | } 78 | ``` 79 | 80 | The `data` object contains the `smsId`, `recipient` and `message` specified above. 81 | 82 | ```javascript 83 | const data = { 84 | smsId, 85 | recipient, 86 | message, 87 | }; 88 | ``` 89 | 90 | We use the axios package for request execution. The 91 | `requestOptions` object contains the parameters `method`, `headers`, `auth`, `baseURL` and `data` (previously referred) which will be used by axios in order to send the desired http post request. The `auth` property takes a username and password and generates an HTTP Basic Auth header (for more information on Basic Auth see our [code example](https://github.com/sipgate-io/sipgateio-basicauth-node)). 92 | 93 | ```javascript 94 | const requestOptions = { 95 | method: "POST", 96 | headers: { 97 | Accept: "application/json", 98 | "Content-Type": "application/json", 99 | }, 100 | auth: { 101 | username: tokenId, 102 | password: token, 103 | }, 104 | data, 105 | }; 106 | ``` 107 | 108 | > If OAuth should be used for `Authorization` instead of Basic Auth we do not suply the auth object in the request options. Instead we set the authorization header to `Bearer` followed by a space and the access token: `` Authorization: `Bearer ${accessToken}`, ``. For an example application interacting with the sipgate API using OAuth see our [sipgate.io Node.js OAuth example](https://github.com/sipgate-io/sipgateio-oauth-node) 109 | 110 | The `axios` instance takes the request URL and `requestOptions` as arguments and process the desired http request. The request URL consists of the base URL defined above and the endpoint `/sessions/sms`. 111 | 112 | ```javascript 113 | axios(`${baseURL}/sessions/sms`, requestOptions); 114 | ``` 115 | 116 | #### Send SMS with custom sender number 117 | 118 | By default 'sipgate' will be used as the sender. It is only possible to change the sender to a mobile phone number by verifying ownership of said number. In order to accomplish this, proceed as follows: 119 | 120 | 1. Log into your [sipgate account](https://app.sipgate.com/connections/sms) 121 | 2. Click **SMS** in the sidebar (if this option is not displayed you might need to book the **Web SMS** feature from the Feature Store) 122 | 3. Click the gear icon on the right side of the **Caller ID** box and enter the desired sender number. 123 | 4. Proceed to follow the instructions on the website to verify the number. 124 | 125 | ### Web SMS Extensions 126 | 127 | A Web SMS extension consists of the letter 's' followed by a number (e.g. 's0'). The sipgate API uses the concept of Web SMS extensions to identify devices within your account that are enabled to send SMS. In this context the term 'device' does not necessarily refer to a hardware phone but rather a virtual connection. 128 | 129 | You can use the sipgate api to find out what your extension is. For example: 130 | 131 | ```bash 132 | curl \ 133 | --user tokenId:token \ 134 | https://api.sipgate.com/v2/{userId}/sms 135 | ``` 136 | 137 | Replace `tokenId` and `token` with your sipgate credentials and `userId` with your sipgate user id. 138 | 139 | The user id consists of the letter 'w' followed by a number (e.g. 'w0'). It can be found as follows: 140 | 141 | 1. Log into your [sipgate account](https://app.sipgate.com) 142 | 2. The URL of the page should have the form `https://app.sipgate.com/{userId}/...` where `{userId}` is your user id. 143 | 144 | ### Common Issues 145 | 146 | #### SMS sent successfully but no message received 147 | 148 | Possible reasons are: 149 | 150 | - incorrect or mistyped phone number 151 | - recipient phone is not connected to network 152 | - long message text - delivery can take a little longer 153 | 154 | #### HTTP Errors 155 | 156 | | reason | errorcode | 157 | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: | 158 | | bad request (e.g. request body fields are empty or only contain spaces, timestamp is invalid etc.) | 400 | 159 | | tokenId and/or token are wrong | 401 | 160 | | insufficient account balance | 402 | 161 | | no permission to use specified SMS extension (e.g. SMS feature not booked or user password must be reset in [web app](https://app.sipgate.com/login)) | 403 | 162 | | wrong REST API endpoint | 404 | 163 | | wrong request method | 405 | 164 | | wrong or missing `Content-Type` header with `application/json` | 415 | 165 | | internal server error or unhandled bad request (e.g. `smsId` not set) | 500 | 166 | 167 | ### Related 168 | 169 | - [axios](https://github.com/axios/axios) 170 | - [sipgate team FAQ (DE)](https://teamhelp.sipgate.de/hc/de) 171 | - [sipgate basic FAQ (DE)](https://basicsupport.sipgate.de/hc/de) 172 | 173 | ### Contact Us 174 | 175 | Please let us know how we can improve this example. 176 | If you have a specific feature request or found a bug, please use **Issues** or fork this repository and send a **pull request** with your improvements. 177 | 178 | ### License 179 | 180 | This project is licensed under **The Unlicense** (see [LICENSE file](./LICENSE)). 181 | 182 | ### External Libraries 183 | 184 | This code uses the following external libraries 185 | 186 | - axios: 187 | Licensed under the [MIT License](https://opensource.org/licenses/MIT) 188 | Website: https://github.com/axios/axios 189 | 190 | --- 191 | 192 | [sipgate.io](https://www.sipgate.io) | [@sipgateio](https://twitter.com/sipgateio) | [API-doc](https://api.sipgate.com/v2/doc) 193 | --------------------------------------------------------------------------------