{"_id":"@artian-techs/adonis-sms","_rev":"3-1807175acd0eb7c19d3a2e936c50ca32","name":"@artian-techs/adonis-sms","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@artian-techs/adonis-sms","version":"1.0.0","keywords":["sms","adonisjs","nodejs","node.js","vonage","twilio","sns","amazon","aws","notification","service","infobip","api","sdk","rest","gateway","unicode","message","text","phone","number","mobile","sender","receiver","recipient"],"author":{"name":"Oussama Benhamed","email":"b.oussama@corposmart.dz"},"license":"MIT","_id":"@artian-techs/adonis-sms@1.0.0","maintainers":[{"name":"artian-techs","email":"artian.technologies@gmail.com"}],"homepage":"https://github.com/Artian-Techs/adonis-sms#readme","bugs":{"url":"https://github.com/Artian-Techs/adonis-sms/issues"},"np":{"tag":"latest","branch":"main","message":"chore(release): %s","anyBranch":false},"dist":{"shasum":"1d173c814cf692f467192427b20beea21a44eb27","tarball":"https://registry.npmjs.org/@artian-techs/adonis-sms/-/adonis-sms-1.0.0.tgz","fileCount":72,"integrity":"sha512-DLBEGXh86enanl6vxRLqKZDe3qqYfn+YbmJZ1SJ/JO7M7VYruZC/j5S2IZ+EWzQ0nAkbh9xGj/+CDWUHvLKhRQ==","signatures":[{"sig":"MEYCIQD1wz0kqAz6C028SkBBoKc7rL9x60b7IMxufyGVsxGiOAIhANyqw9N4gQy8FE63FQwADWyt89JXREyE7xufVuWZr1gw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215072},"main":"build/index.js","tsup":{"dts":false,"clean":true,"entry":["index.ts","configure.ts","stubs/main.ts","src/types.ts","services/main.ts","providers/sms_provider.ts","commands/make_sms.ts","src/drivers/sns.ts","src/drivers/twilio.ts","src/drivers/vonage.ts","src/drivers/infobip.ts","src/messengers/memory_queue.ts"],"format":"esm","outDir":"./build","target":"esnext","sourcemap":true},"type":"module","_from":"file:artian-techs-adonis-sms-1.0.0.tgz","types":"./build/index.d.ts","engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./drivers/sns":"./build/src/drivers/sns.js","./sms_provider":"./build/providers/sms_provider.js","./services/main":"./build/services/main.js","./drivers/twilio":"./build/src/drivers/twilio.js","./drivers/vonage":"./build/src/drivers/vonage.js","./drivers/infobip":"./build/src/drivers/infobip.js","./messengers/memory":"./build/src/messengers/memory_queue.js"},"scripts":{"lint":"eslint .","test":"npm run quick:test","build":"npm run compile","clean":"del-cli build","format":"prettier --write .","compile":"tsup-node && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"np --message=\"chore(release): %s\"","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint && npm run clean","quick:test":"node --import=./tsnode.esm.js --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/**/*.stub\" build","index:commands":"adonis-kit index build/commands"},"_npmUser":{"name":"artian-techs","email":"artian.technologies@gmail.com"},"prettier":"@adonisjs/prettier-config","_resolved":"/tmp/77b5d6fdb805c54e5e266ffa556dfe22/artian-techs-adonis-sms-1.0.0.tgz","_integrity":"sha512-DLBEGXh86enanl6vxRLqKZDe3qqYfn+YbmJZ1SJ/JO7M7VYruZC/j5S2IZ+EWzQ0nAkbh9xGj/+CDWUHvLKhRQ==","repository":{"url":"git+ssh://git@github.com/Artian-Techs/adonis-sms.git","type":"git"},"_npmVersion":"11.17.0","description":"Send SMS from AdonisJS using some known services","directories":{},"_nodeVersion":"24.19.0","dependencies":{"fastq":"^1.17.1","@poppinss/utils":"^6.10.1"},"publishConfig":{"tag":"latest","access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"np":"^10.0.2","tsup":"^8.5.1","dotenv":"^16.4.5","eslint":"^9.20.0","twilio":"^5.0.0","del-cli":"^5.1.0","ts-node":"^10.9.2","prettier":"^3.2.5","copyfiles":"^2.4.1","typescript":"^5.4.3","@types/node":"^24.13.3","@japa/assert":"^3.0.0","@japa/runner":"^3.1.4","@adonisjs/core":"^6.21.0","@infobip-api/sdk":"^0.3.2","@japa/expect-type":"^2.0.2","@japa/file-system":"^2.3.0","@adonisjs/tsconfig":"^1.4.1","@vonage/server-sdk":"^3.15.0","@adonisjs/assembler":"^7.8.2","@aws-sdk/client-sns":"^3.540.0","@adonisjs/eslint-config":"^2.1.2","@adonisjs/prettier-config":"^1.5.0"},"peerDependencies":{"twilio":"^5.0.0","@adonisjs/core":"^6.2.0 || ^7.0.0","@infobip-api/sdk":"^0.3.0","@vonage/server-sdk":"^3.15.0","@aws-sdk/client-sns":"^3.540.0"},"peerDependenciesMeta":{"twilio":{"optional":true},"@infobip-api/sdk":{"optional":true},"@vonage/server-sdk":{"optional":true},"@aws-sdk/client-sns":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-sms_1.0.0_1787060188324_0.16143575224247808","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@artian-techs/adonis-sms","version":"1.0.1","keywords":["sms","adonisjs","nodejs","node.js","vonage","twilio","sns","amazon","aws","notification","service","infobip","api","sdk","rest","gateway","unicode","message","text","phone","number","mobile","sender","receiver","recipient"],"author":{"name":"Oussama Benhamed","email":"b.oussama@corposmart.dz"},"license":"MIT","_id":"@artian-techs/adonis-sms@1.0.1","maintainers":[{"name":"artian-techs","email":"artian.technologies@gmail.com"}],"homepage":"https://github.com/Artian-Techs/adonis-sms#readme","bugs":{"url":"https://github.com/Artian-Techs/adonis-sms/issues"},"np":{"tag":"latest","branch":"main","message":"chore(release): %s","anyBranch":false},"dist":{"shasum":"f47ed627bc0abf4689650ce1e02c67e3464e6809","tarball":"https://registry.npmjs.org/@artian-techs/adonis-sms/-/adonis-sms-1.0.1.tgz","fileCount":72,"integrity":"sha512-JPeGinQNbwHOPszkrc2BNgLNXqSWSQILV8M88QuyjB9RvGt7aqYwVDNMm5J3btz9Bo/jQeB8gfsZt2Y2OYzHyg==","signatures":[{"sig":"MEQCIErJeevM0aLfE2W2GIzMYTqrtu8tvOui/Z/hskg7U5D3AiBWtXrevBBMrUrscNaYFmW1Iq9+jcrJE7xJ2Uj+ij3GCA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215894},"main":"build/index.js","tsup":{"dts":false,"clean":true,"entry":["index.ts","configure.ts","stubs/main.ts","src/types.ts","services/main.ts","providers/sms_provider.ts","commands/make_sms.ts","src/drivers/sns.ts","src/drivers/twilio.ts","src/drivers/vonage.ts","src/drivers/infobip.ts","src/messengers/memory_queue.ts"],"format":"esm","outDir":"./build","target":"esnext","sourcemap":true},"type":"module","_from":"file:artian-techs-adonis-sms-1.0.1.tgz","types":"./build/index.d.ts","engines":{"node":">=24.0.0"},"exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./drivers/sns":"./build/src/drivers/sns.js","./sms_provider":"./build/providers/sms_provider.js","./services/main":"./build/services/main.js","./drivers/twilio":"./build/src/drivers/twilio.js","./drivers/vonage":"./build/src/drivers/vonage.js","./drivers/infobip":"./build/src/drivers/infobip.js","./messengers/memory":"./build/src/messengers/memory_queue.js"},"scripts":{"lint":"eslint .","test":"npm run quick:test","build":"npm run compile","clean":"del-cli build","format":"prettier --write .","compile":"tsup-node && tsc --emitDeclarationOnly --declaration","pretest":"npm run lint","release":"np --message=\"chore(release): %s\"","version":"npm run build","typecheck":"tsc --noEmit","precompile":"npm run lint && npm run clean","quick:test":"node --import=./tsnode.esm.js --enable-source-maps bin/test.ts","postcompile":"npm run copy:templates && npm run index:commands","copy:templates":"copyfiles --up=1 \"stubs/**/**/*.stub\" build","index:commands":"adonis-kit index build/commands"},"_npmUser":{"name":"artian-techs","email":"artian.technologies@gmail.com"},"prettier":"@adonisjs/prettier-config","_resolved":"/tmp/b09adf1cd89e81b7ef07b9799dd98f63/artian-techs-adonis-sms-1.0.1.tgz","_integrity":"sha512-JPeGinQNbwHOPszkrc2BNgLNXqSWSQILV8M88QuyjB9RvGt7aqYwVDNMm5J3btz9Bo/jQeB8gfsZt2Y2OYzHyg==","repository":{"url":"git+ssh://git@github.com/Artian-Techs/adonis-sms.git","type":"git"},"_npmVersion":"11.17.0","description":"Send SMS from AdonisJS using some known services","directories":{},"_nodeVersion":"24.19.0","dependencies":{"fastq":"^1.17.1","@poppinss/utils":"^6.10.1"},"publishConfig":{"tag":"latest","access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"np":"^10.0.2","tsup":"^8.5.1","dotenv":"^16.4.5","eslint":"^9.20.0","twilio":"^5.0.0","del-cli":"^5.1.0","ts-node":"^10.9.2","prettier":"^3.2.5","copyfiles":"^2.4.1","typescript":"^5.4.3","@types/node":"^24.13.3","@japa/assert":"^3.0.0","@japa/runner":"^3.1.4","@adonisjs/core":"^6.21.0","@infobip-api/sdk":"^0.3.2","@japa/expect-type":"^2.0.2","@japa/file-system":"^2.3.0","@adonisjs/tsconfig":"^1.4.1","@vonage/server-sdk":"^3.15.0","@adonisjs/assembler":"^7.8.2","@aws-sdk/client-sns":"^3.540.0","@adonisjs/eslint-config":"^2.1.2","@adonisjs/prettier-config":"^1.5.0"},"peerDependencies":{"twilio":"^5.0.0","@adonisjs/core":"^6.2.0 || ^7.0.0","@infobip-api/sdk":"^0.3.0","@vonage/server-sdk":"^3.15.0","@aws-sdk/client-sns":"^3.540.0"},"peerDependenciesMeta":{"twilio":{"optional":true},"@infobip-api/sdk":{"optional":true},"@vonage/server-sdk":{"optional":true},"@aws-sdk/client-sns":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/adonis-sms_1.0.1_1787079240862_0.1365868240287207","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@artian-techs/adonis-sms","version":"1.0.2","description":"Send SMS from AdonisJS using some known services","engines":{"node":">=24.0.0"},"main":"build/index.js","type":"module","exports":{".":"./build/index.js","./types":"./build/src/types.js","./commands":"./build/commands/main.js","./services/main":"./build/services/main.js","./sms_provider":"./build/providers/sms_provider.js","./drivers/sns":"./build/src/drivers/sns.js","./drivers/twilio":"./build/src/drivers/twilio.js","./drivers/vonage":"./build/src/drivers/vonage.js","./drivers/infobip":"./build/src/drivers/infobip.js","./messengers/memory":"./build/src/messengers/memory_queue.js"},"keywords":["sms","adonisjs","nodejs","node.js","vonage","twilio","sns","amazon","aws","notification","service","infobip","api","sdk","rest","gateway","unicode","message","text","phone","number","mobile","sender","receiver","recipient"],"repository":{"type":"git","url":"git+ssh://git@github.com/Artian-Techs/adonis-sms.git"},"author":{"name":"Oussama Benhamed","email":"b.oussama@corposmart.dz"},"license":"MIT","bugs":{"url":"https://github.com/Artian-Techs/adonis-sms/issues"},"homepage":"https://github.com/Artian-Techs/adonis-sms#readme","dependencies":{"@poppinss/utils":"^6.10.1","fastq":"^1.17.1"},"peerDependencies":{"@adonisjs/core":"^6.2.0 || ^7.0.0","@aws-sdk/client-sns":"^3.540.0","@infobip-api/sdk":"^0.3.0","@vonage/server-sdk":"^3.15.0","twilio":"^5.0.0"},"peerDependenciesMeta":{"@aws-sdk/client-sns":{"optional":true},"@infobip-api/sdk":{"optional":true},"@vonage/server-sdk":{"optional":true},"twilio":{"optional":true}},"devDependencies":{"@adonisjs/assembler":"^7.8.2","@adonisjs/core":"^6.21.0","@adonisjs/eslint-config":"^2.1.2","@adonisjs/prettier-config":"^1.5.0","@adonisjs/tsconfig":"^1.4.1","@aws-sdk/client-sns":"^3.540.0","@infobip-api/sdk":"^0.3.2","@japa/assert":"^3.0.0","@japa/expect-type":"^2.0.2","@japa/file-system":"^2.3.0","@japa/runner":"^3.1.4","@types/node":"^24.13.3","@vonage/server-sdk":"^3.15.0","copyfiles":"^2.4.1","del-cli":"^5.1.0","dotenv":"^16.4.5","eslint":"^9.20.0","np":"^10.0.2","prettier":"^3.2.5","ts-node":"^10.9.2","tsup":"^8.5.1","twilio":"^5.0.0","typescript":"^5.4.3"},"publishConfig":{"access":"public","tag":"latest","registry":"https://registry.npmjs.org"},"prettier":"@adonisjs/prettier-config","np":{"message":"chore(release): %s","tag":"latest","branch":"main","anyBranch":false},"tsup":{"entry":["index.ts","configure.ts","stubs/main.ts","src/types.ts","services/main.ts","providers/sms_provider.ts","commands/make_sms.ts","src/drivers/sns.ts","src/drivers/twilio.ts","src/drivers/vonage.ts","src/drivers/infobip.ts","src/messengers/memory_queue.ts"],"outDir":"./build","clean":true,"format":"esm","dts":false,"sourcemap":true,"target":"esnext"},"scripts":{"clean":"del-cli build","copy:templates":"copyfiles --up=1 \"stubs/**/**/*.stub\" build","index:commands":"adonis-kit index build/commands","typecheck":"tsc --noEmit","lint":"eslint .","format":"prettier --write .","quick:test":"node --import=./tsnode.esm.js --enable-source-maps bin/test.ts","pretest":"npm run lint","test":"npm run quick:test","precompile":"npm run lint && npm run clean","compile":"tsup-node && tsc --emitDeclarationOnly --declaration","postcompile":"npm run copy:templates && npm run index:commands","build":"npm run compile","version":"npm run build","release":"np --message=\"chore(release): %s\""},"types":"./build/index.d.ts","_id":"@artian-techs/adonis-sms@1.0.2","_integrity":"sha512-xF9IAALIYDm8mT0IwdnFXooaU7ht6D4bslfRystsCSBspmGTSqjR76sCVKrcbwvoPMHuX1Odw/KE9GG7FQQDKA==","_resolved":"/tmp/ae4c2debe99bc76b239d4bca4cd77193/artian-techs-adonis-sms-1.0.2.tgz","_from":"file:artian-techs-adonis-sms-1.0.2.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-xF9IAALIYDm8mT0IwdnFXooaU7ht6D4bslfRystsCSBspmGTSqjR76sCVKrcbwvoPMHuX1Odw/KE9GG7FQQDKA==","shasum":"4be7059620c8944242af309dc923abf65b92b09c","tarball":"https://registry.npmjs.org/@artian-techs/adonis-sms/-/adonis-sms-1.0.2.tgz","fileCount":72,"unpackedSize":215907,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGpc8p0wuwPyPXMqhoNO2MnF5s5ax22+8iLPPxbVKbHcAiAUp/4tR7bKRMwp+RZi7ls8ZTVpPu8740MZit9Jd6XGgg=="}]},"_npmUser":{"name":"artian-techs","email":"artian.technologies@gmail.com"},"directories":{},"maintainers":[{"name":"artian-techs","email":"artian.technologies@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/adonis-sms_1.0.2_1787084870394_0.042493567693782985"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T13:36:28.051Z","modified":"2026-08-18T20:27:50.696Z","1.0.0":"2026-08-18T13:36:28.474Z","1.0.1":"2026-08-18T18:54:01.037Z","1.0.2":"2026-08-18T20:27:50.537Z"},"bugs":{"url":"https://github.com/Artian-Techs/adonis-sms/issues"},"author":{"name":"Oussama Benhamed","email":"b.oussama@corposmart.dz"},"license":"MIT","homepage":"https://github.com/Artian-Techs/adonis-sms#readme","keywords":["sms","adonisjs","nodejs","node.js","vonage","twilio","sns","amazon","aws","notification","service","infobip","api","sdk","rest","gateway","unicode","message","text","phone","number","mobile","sender","receiver","recipient"],"repository":{"type":"git","url":"git+ssh://git@github.com/Artian-Techs/adonis-sms.git"},"description":"Send SMS from AdonisJS using some known services","maintainers":[{"name":"artian-techs","email":"artian.technologies@gmail.com"}],"readme":"# @artian-techs/adonis-sms\n\n> Send SMS from AdonisJS using some known services\n\n[![github-actions-image]][github-actions-url] [![npm-image]][npm-url] [![license-image]][license-url] [![typescript-image]][typescript-url]\n\nShips with drivers for **Twilio**, **Vonage** (Messages API), **Amazon SNS** and **Infobip**, and lets you plug in your own.\n\n> **Requires AdonisJS 6 or 7, on Node 24 or above.**\n>\n> Note that AdonisJS 6 itself supports Node 20, while this package does not.\n> Running it on an AdonisJS 6 application therefore means running that\n> application on Node 24.\n\n## Table of contents\n\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Sending messages](#sending-messages)\n  - [Selecting a client](#selecting-a-client)\n  - [Runtime options](#runtime-options)\n  - [Sending in the background](#sending-in-the-background)\n  - [Many recipients, one message](#many-recipients-one-message)\n  - [Many recipients, many messages](#many-recipients-many-messages)\n- [Class based messages](#class-based-messages)\n- [Responses](#responses)\n- [Testing](#testing)\n- [Sender and webhook resolution](#sender-and-webhook-resolution)\n- [Events](#events)\n- [Replacing the queue](#replacing-the-queue)\n- [Writing a custom driver](#writing-a-custom-driver)\n- [Driver reference](#driver-reference)\n- [API reference](#api-reference)\n\n## Installation\n\n```sh\nnode ace add @artian-techs/adonis-sms\n```\n\nOr install and configure it manually:\n\n```sh\nnpm i @artian-techs/adonis-sms\nnode ace configure @artian-techs/adonis-sms\n```\n\nThe configure hook asks which services you want to use, then:\n\n- creates `config/sms.ts`\n- registers the provider and the commands inside `adonisrc.ts`\n- adds the required environment variables to `.env` and `start/env.ts`\n- installs the SDK of every selected service\n\nYou can skip the prompt with the `--drivers` flag:\n\n```sh\nnode ace configure @artian-techs/adonis-sms --drivers=twilio --drivers=vonage\n```\n\nEach driver's SDK is an **optional peer dependency** loaded on demand, so you only install what you actually use.\n\n## Configuration\n\nEvery client is declared through a driver helper. The `SmsClientsList` interface is augmented with the result of `InferSmsClients`, which is what makes `sms.use()` fully typed.\n\n```ts\n// config/sms.ts\nimport env from '#start/env'\nimport { defineConfig, drivers } from '@artian-techs/adonis-sms'\nimport type { InferSmsClients } from '@artian-techs/adonis-sms/types'\n\nconst smsConfig = defineConfig({\n  /**\n   * The client used when none is explicitly selected\n   */\n  default: 'twilio',\n\n  /**\n   * Global sender ID, used by every driver that does not define its own\n   */\n  from: 'AdonisJS',\n\n  /**\n   * Replace consecutive whitespaces inside the message body by a single space\n   */\n  trim: true,\n\n  /**\n   * Remove accents from the message body\n   */\n  normalize: false,\n\n  /**\n   * Global webhook URL for delivery reports\n   */\n  webhook: 'https://example.com/sms/webhook',\n\n  clients: {\n    twilio: drivers.twilio({\n      accountSid: env.get('TWILIO_ACCOUNT_SID'),\n      authToken: env.get('TWILIO_AUTH_TOKEN'),\n      from: env.get('TWILIO_PHONE_NUMBER'),\n    }),\n\n    vonage: drivers.vonage({\n      apiKey: env.get('VONAGE_API_KEY'),\n      apiSecret: env.get('VONAGE_API_SECRET'),\n    }),\n  },\n})\n\nexport default smsConfig\n\ndeclare module '@artian-techs/adonis-sms/types' {\n  export interface SmsClientsList extends InferSmsClients<typeof smsConfig> {}\n}\n```\n\n### Global options\n\n| Option      | Type      | Description                                                            |\n| ----------- | --------- | ---------------------------------------------------------------------- |\n| `default`   | `string`  | Name of the client used when `use()` is called without an argument      |\n| `clients`    | `object`  | The clients available to your application                               |\n| `from`      | `string`  | Fallback sender for every client                                        |\n| `webhook`   | `string`  | Fallback delivery report URL for every client that supports one         |\n| `concurrency` | `number` | How many requests a fan-out performs at once. Defaults to 10          |\n| `trim`      | `boolean` | Condense consecutive whitespaces in the message body                   |\n| `normalize` | `boolean` | Strip accents from the message body                                    |\n\nThe same driver can back several clients with a different config — for example a `transactional` and a `promotional` client both using Twilio:\n\n```ts\nclients: {\n  transactional: drivers.twilio({ /* ... */ from: env.get('TWILIO_TRANSACTIONAL_NUMBER') }),\n  promotional: drivers.twilio({ /* ... */ from: env.get('TWILIO_PROMOTIONAL_NUMBER') }),\n}\n```\n\n## Sending messages\n\nImport the sms service and compose the message through the callback. Recipients must use the [E.164](https://en.wikipedia.org/wiki/E.164) format — anything else throws `E_INVALID_PHONE_NUMBER`.\n\n```ts\nimport sms from '@artian-techs/adonis-sms/services/main'\n\nawait sms.send((message) => {\n  message.to('+12121212121').message('Hello world')\n})\n```\n\nThe message builder exposes three chainable methods:\n\n```ts\nmessage.from('AdonisJS').to('+12121212121').message('Hello world')\n```\n\n### Selecting a client\n\n```ts\nawait sms.use('vonage').send((message) => {\n  message.to('+12121212121').message('Hello world')\n})\n```\n\nSmsClient instances are cached for the lifetime of the process, so repeated `use()` calls return the same object. Passing a name that is not in your config throws at runtime, and does not compile in the first place.\n\n### Runtime options\n\nThe second argument is forwarded to the driver and is typed against the **selected** client, so Twilio options are not accepted by the Vonage client and vice versa.\n\n```ts\nawait sms.use('twilio').send(\n  (message) => {\n    message.to('+12121212121').message('Hello world')\n  },\n  { statusCallback: 'https://example.com/sms/webhook', validityPeriod: 3600 }\n)\n```\n\n### Sending in the background\n\n`sendLater` pushes the message to an in-memory queue and resolves immediately, without waiting for the provider.\n\n```ts\nawait sms.sendLater((message) => {\n  message.to('+12121212121').message('Hello world')\n})\n```\n\nBecause delivery happens after the call returns, failures are reported through the [`queued:sms:error`](#events) event rather than a rejected promise.\n\n### Many recipients, one message\n\n```ts\nawait sms.sendMany((message) => {\n  message.to(['+12121212121', '+13131313131']).message('Hello world')\n})\n```\n\n`to` may be called several times and accepts a single number or an array. Duplicate recipients are removed:\n\n```ts\nmessage.to('+12121212121').to(['+13131313131', '+14141414141'])\n```\n\nInfobip accepts multiple destinations in one API call and uses it. The others — Twilio, Vonage and Amazon SNS — fan out into concurrent `send` calls, **at most `concurrency` at a time** (10 by default). An unbounded fan-out over a large audience exhausts sockets and trips provider rate limits, so the bound is deliberate. Raise or lower it globally, or per driver:\n\n```ts\nvonage: drivers.vonage({ apiKey: env.get('VONAGE_API_KEY'), concurrency: 25 })\n```\n\n### Many recipients, many messages\n\n```ts\nawait sms.sendBulk((message) => {\n  message.to([\n    ['+12121212121', 'Hello Jane'],\n    ['+13131313131', 'Hello John'],\n  ])\n})\n```\n\nNo built-in driver currently exposes a native bulk endpoint, so this fans out into one `send` call per pair. A [custom driver](#writing-a-custom-driver) may implement `sendBulk` to use its provider's own batch endpoint.\n\n## Class based messages\n\nCreate a reusable, self contained message:\n\n```sh\nnode ace make:sms notify_user\n```\n\n```ts\n// app/sms/notify_user.ts\nimport { BaseSms } from '@artian-techs/adonis-sms'\n\nexport default class NotifyUser extends BaseSms {\n  from = 'AdonisJS'\n\n  constructor(private user: User) {\n    super()\n  }\n\n  /**\n   * Called automatically when the message is sent or queued. It may be async\n   */\n  prepare() {\n    this.message.to(this.user.phoneNumber).message('Your order has been shipped')\n  }\n}\n```\n\n```ts\nimport sms from '@artian-techs/adonis-sms/services/main'\nimport NotifyUser from '#clients/notify_user'\n\nawait new NotifyUser(user).send(sms.use())\nawait new NotifyUser(user).sendLater(sms.use())\n```\n\n`prepare` runs only once per instance, so sending the same object twice does not rebuild the message.\n\n## Testing\n\n`sms.fake()` swaps every client for one that records messages instead of\ndelivering them, so your test suite never contacts a provider.\n\n```ts\nimport { test } from '@japa/runner'\nimport sms from '@artian-techs/adonis-sms/services/main'\n\ntest('sends a welcome sms on signup', async ({ client }) => {\n  const fake = sms.fake()\n\n  await client.post('/register').json({ phone: '+12121212121' })\n\n  fake.assertSent((message) => message.message.to === '+12121212121')\n\n  sms.restore()\n})\n```\n\nAlways call `sms.restore()` afterwards — a group `teardown` hook is the usual\nplace — otherwise the fake leaks into the next test.\n\n| Assertion | Passes when |\n| --- | --- |\n| `assertSent(finder?)` | at least one message matches |\n| `assertNotSent(finder?)` | no message matches |\n| `assertSentCount(n, finder?)` | exactly `n` messages match |\n| `assertNoneSent()` | nothing was sent at all |\n| `assertQueued(finder?)` | at least one message went through `sendLater` |\n\nThe predicate receives `{ message, config, queued }`, where `message` is the\ncompiled `{ from, to, message }` node. To inspect rather than assert, use\n`fake.sentMessages`, `fake.find(finder)` and `fake.filter(finder)`, and\n`fake.clear()` to forget what was recorded.\n\n## Sender and webhook resolution\n\nThe sender is resolved in this order, first match wins:\n\n1. the `from` set on the message\n2. the `from` set on the driver config\n3. the global `from` in `config/sms.ts`\n\nIf none is defined, `E_NO_SENDER_PROVIDED` is thrown. The webhook URL follows the same order — runtime options, then driver config, then global config — and is validated before use (`E_INVALID_WEBHOOK_FORMAT`, `E_INVALID_WEBHOOK_PROTOCOL`). It is only forwarded to drivers that report deliveries over HTTP, which is every built-in driver except Amazon SNS.\n\nEach driver maps the resolved URL onto its own parameter name, so you never have to remember whether the provider calls it `callback`, `statusCallback`, `notifyUrl`, `report_url` or `notify_url`.\n\n## Responses\n\nEvery driver returns the same `SmsResponse` object, whatever the provider:\n\n```ts\nconst response = await sms.send((message) => {\n  message.to('+12121212121').message('Hello world')\n})\n\nresponse.messageId // string — normalized across every provider\nresponse.original // the raw payload the provider returned for this message\n```\n\nOnly the identifier is normalized. Providers disagree on everything else — and\nin particular, at send time almost all of them merely acknowledge the message,\nthe real delivery status arriving later on your webhook — so no unified\n`status` is exposed. Anything provider specific stays reachable through\n`original`, which is fully typed:\n\n```ts\nconst response = await sms.use('twilio').send(/* ... */)\nresponse.original.numSegments // ✅ typed as a Twilio MessageInstance\n\nconst infobip = await sms.use('infobip').send(/* ... */)\ninfobip.original.status // ✅ typed as an Infobip message entry\n```\n\n`messageId` maps to `sid` on Twilio, `MessageId` on Amazon SNS, `messageUUID` on\nthe Vonage Messages API, and `messageId` on the legacy Vonage API and Infobip.\n\n### Batch responses\n\n`sendMany` and `sendBulk` return `SmsResponse[]`. **The array length is not the\nrecipient count and the array must never be indexed by recipient** — its length\nreflects what the provider actually reported:\n\n| Providers | Entries |\n| --- | --- |\n| Twilio, Vonage (both drivers), Amazon SNS | one per recipient — the driver sends one request each |\n| Infobip | one per entry in the provider's own list |\n\nA driver must never pad that array to match the recipient count: inventing a\nper-recipient confirmation the provider never gave would be worse than a short\narray. A provider acknowledging a whole batch with a single identifier should\nyield a single entry.\n\n## Events\n\n| Event              | Payload                            | Emitted                                       |\n| ------------------ | ---------------------------------- | --------------------------------------------- |\n| `sms:sending`      | `{ clientName, message }`           | Before handing the message to the driver      |\n| `sms:sent`         | `{ clientName, message, response }` | After the driver resolved                     |\n| `queued:sms:error` | `{ clientName, error }`             | A background message could not be delivered   |\n\n```ts\nimport emitter from '@adonisjs/core/services/emitter'\nimport logger from '@adonisjs/core/services/logger'\n\nemitter.on('queued:sms:error', ({ clientName, error }) => {\n  logger.error({ clientName, err: error }, 'Unable to deliver the sms')\n})\n```\n\n## Replacing the queue\n\n`sendLater` uses an in-memory queue that keeps messages in the process and delivers them in chunks of 10. It is deliberately simple: pending messages are lost if the process restarts. To hand them to a real queue instead, register your own messenger.\n\n```ts\n// start/sms.ts\nimport sms from '@artian-techs/adonis-sms/services/main'\nimport queue from '@rlanz/bull-queue/services/main'\n\nsms.setMessenger(() => ({\n  async queue(compiledSms) {\n    await queue.dispatch(SendSmsJob, compiledSms)\n  },\n}))\n```\n\n`compiledSms` is a `CompiledSmsNode` — `{ message, clientName, config }` — which is plain JSON and therefore safe to serialize. In your job, replay it through the client it came from:\n\n```ts\nimport type { CompiledSmsNode, SmsClientsList } from '@artian-techs/adonis-sms/types'\n\nasync function handle(compiledSms: CompiledSmsNode) {\n  /**\n   * \"clientName\" is a plain string on the serialized payload, so it has to be\n   * narrowed back to a known client name\n   */\n  await sms.use(compiledSms.clientName as keyof SmsClientsList).sendCompiled(compiledSms)\n}\n```\n\nThe messenger is applied to every client, including the ones already resolved from the cache. A single client can also get its own messenger with `sms.use('twilio').setMessenger(messenger)`.\n\n## Writing a custom driver\n\nA driver is any object satisfying `SmsDriverContract`. In practice you extend `BaseDriver`, which handles the config plumbing and gives you an HTTP helper.\n\n### The contract\n\n```ts\ninterface SmsDriverContract {\n  /**\n   * Whether the driver can receive delivery reports on a webhook URL. When\n   * false, the resolved webhook is never forwarded to the driver\n   */\n  acceptWebhook: boolean\n\n  /**\n   * Returns the config the driver was created with. Used to resolve the\n   * sender and the webhook\n   */\n  getConfig(): SmsDriverConfig\n\n  /**\n   * Send one message to one recipient\n   */\n  send(message: MessageNode, config?: any): Promise<SmsResponse<any>>\n\n  /**\n   * Send the same message to many recipients.\n   *\n   * The returned array holds one entry per message the provider reported,\n   * so it must never be indexed by recipient\n   */\n  sendMany(message: ManyMessagesNode, config?: any): Promise<SmsResponse<any>[]>\n\n  /**\n   * Optional. Send a different message to every recipient in a single call.\n   * When absent, \"sendBulk\" falls back to multiple \"send\" calls\n   */\n  sendBulk?(message: BulkMessageNode, config?: any): Promise<SmsResponse<any>[]>\n}\n```\n\n`send` receives a `MessageNode` whose `from` is already resolved and whose `to` has already been validated, so a driver never has to re-check either.\n\n### A complete example\n\n```ts\n// app/sms/drivers/my_provider.ts\nimport { BaseDriver, SmsResponse } from '@artian-techs/adonis-sms'\nimport type {\n  SmsDriverConfig,\n  MessageNode,\n  ManyMessagesNode,\n} from '@artian-techs/adonis-sms/types'\n\n/**\n * The config your driver accepts inside \"config/sms.ts\". Extending\n * SmsDriverConfig gives you the shared \"from\" and \"webhook\" options\n */\nexport type MyProviderConfig = SmsDriverConfig & {\n  apiKey: string\n  baseUrl?: string\n}\n\n/**\n * The options accepted as the second argument of \"send\"\n */\nexport type MyProviderRuntimeOptions = {\n  webhook?: string\n  priority?: 'normal' | 'high'\n}\n\n/**\n * The shape your provider returns\n */\nexport type MyProviderResponse = {\n  id: string\n  status: string\n}\n\nexport class MyProviderDriver extends BaseDriver {\n  constructor(protected config: MyProviderConfig) {\n    super(config)\n  }\n\n  async send(\n    { from, to, message }: MessageNode,\n    options?: MyProviderRuntimeOptions\n  ): Promise<SmsResponse<MyProviderResponse>> {\n    const endpoint = this.config.baseUrl ?? 'https://api.my-provider.com/v1/messages'\n\n    const httpResponse = await fetch(endpoint, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'Authorization': `Bearer ${this.config.apiKey}`,\n      },\n      body: JSON.stringify({\n        sender: from,\n        recipient: to,\n        text: message,\n        priority: options?.priority ?? 'normal',\n        /**\n         * The webhook is already resolved from the runtime options, the\n         * driver config and the global config, in that order\n         */\n        callback_url: options?.webhook,\n      }),\n    })\n\n    const response = (await httpResponse.json()) as MyProviderResponse\n\n    /**\n     * Wrap the raw payload so callers get the same shape as every other\n     * driver, without losing anything the provider returned\n     */\n    return new SmsResponse(response.id, response)\n  }\n\n  async sendMany(\n    { from, to, message }: ManyMessagesNode,\n    options?: MyProviderRuntimeOptions\n  ): Promise<SmsResponse<MyProviderResponse>[]> {\n    return Promise.all(to.map((recipient) => this.send({ from, to: recipient, message }, options)))\n  }\n}\n```\n\nNote the `SmsResponse` wrapper: it is what makes the response shape uniform\nacross providers. `messageId` is the identifier your provider assigned, and\n`original` keeps its raw payload reachable and typed.\n\nThe package ships no HTTP helper: use the native `fetch`, your own HTTP client, or the provider's SDK. Nothing in the contract requires any of them — `BaseDriver` only stores the config and exposes it through `getConfig()`.\n\n### Registering it\n\nA driver is registered as a plain factory function. No `extend` call, no service provider.\n\n```ts\n// config/sms.ts\nimport { defineConfig, drivers } from '@artian-techs/adonis-sms'\nimport { MyProviderDriver } from '../app/sms/drivers/my_provider.js'\n\nconst smsConfig = defineConfig({\n  default: 'myProvider',\n  from: 'AdonisJS',\n  clients: {\n    myProvider: () => new MyProviderDriver({ apiKey: env.get('MY_PROVIDER_API_KEY') }),\n\n    twilio: drivers.twilio({\n      accountSid: env.get('TWILIO_ACCOUNT_SID'),\n      authToken: env.get('TWILIO_AUTH_TOKEN'),\n      from: env.get('TWILIO_PHONE_NUMBER'),\n    }),\n  },\n})\n```\n\nThat is all it takes for the driver to become type-safe: `InferSmsClients` picks it up from the config, so `sms.use('myProvider')` returns a client whose runtime options are `MyProviderRuntimeOptions` and whose `send` resolves to `MyProviderResponse`.\n\n```ts\nconst response = await sms.use('myProvider').send(\n  (message) => message.to('+12121212121').message('Hello world'),\n  { priority: 'high' } // ✅ typed\n)\n\nresponse.messageId // ✅ string\nresponse.original.status // ✅ typed as MyProviderResponse\n```\n\n### Lazy loading an SDK\n\nIf your driver depends on a heavy SDK, import it on demand so applications that do not use the driver never pay for it — this is what the built-in drivers do.\n\n```ts\nexport class MyProviderDriver extends BaseDriver {\n  #client?: MyProviderClient\n\n  async #getClient() {\n    if (!this.#client) {\n      const { MyProviderClient } = await import('my-provider-sdk')\n      this.#client = new MyProviderClient(this.config.apiKey)\n    }\n\n    return this.#client\n  }\n\n  async send(message: MessageNode) {\n    const client = await this.#getClient()\n\n    return client.messages.create({ /* ... */ })\n  }\n}\n```\n\n### Notes\n\n- Set `acceptWebhook = false` if your provider has no delivery report URL. The resolved webhook is then never passed to the driver, and no URL validation is performed.\n- Implement `sendBulk` only when the provider has a real bulk endpoint. Otherwise leave it out and let the fallback fan out into `send` calls.\n- Throw a descriptive error when the provider reports a failure with a `2xx` status. `errors.E_SMS_DRIVER_ERROR` is exported for this purpose.\n\n```ts\nimport { errors } from '@artian-techs/adonis-sms'\n\nif (response.status !== 'accepted') {\n  throw new errors.E_SMS_DRIVER_ERROR([response.reason])\n}\n```\n\n### Distributing it as a package\n\nNothing above is specific to an in-app driver. To ship one on npm, export a config helper alongside the driver so consumers get the same ergonomics as the built-in ones:\n\n```ts\nimport { configProvider } from '@adonisjs/core'\nimport type { ConfigProvider } from '@adonisjs/core/types'\n\nexport function myProvider(\n  config: MyProviderConfig\n): ConfigProvider<() => MyProviderDriver> {\n  return configProvider.create(async () => {\n    const { MyProviderDriver } = await import('./driver.js')\n\n    return () => new MyProviderDriver(config)\n  })\n}\n```\n\n```ts\nclients: {\n  myProvider: myProvider({ apiKey: env.get('MY_PROVIDER_API_KEY') }),\n}\n```\n\n## Driver reference\n\nAll drivers accept the shared `from` and `webhook` options on top of the ones listed below.\n\n### Twilio — `drivers.twilio`\n\n| Option                | Type       | Required |\n| --------------------- | ---------- | -------- |\n| `accountSid`          | `string`   | yes      |\n| `authToken`           | `string`   | yes      |\n| `from`                | `string`   | yes      |\n| `edge`                | `string`   | no       |\n| `region`              | `string`   | no       |\n| `lazyLoading`         | `boolean`  | no       |\n| `userAgentExtensions` | `string[]` | no       |\n\nAny other option accepted by Twilio's `messages.create` may be passed in the config or at runtime. The webhook is mapped to `statusCallback`.\n\n### Vonage — `drivers.vonage`\n\nTargets the **Messages API**, which supersedes Vonage's legacy SMS API. The\nlegacy endpoint is not supported: on an account whose dashboard \"Default SMS\nSetting\" is Messages API, it rejects the very same credentials with\n`Bad Credentials`.\n\n| Option              | Type                            | Required |\n| ------------------- | ------------------------------- | -------- |\n| `apiKey`            | `string`                        | no\\*     |\n| `apiSecret`         | `string`                        | no\\*     |\n| `applicationId`     | `string`                        | no\\*     |\n| `privateKey`        | `string \\| Buffer`              | no\\*     |\n| `encodingType`      | `'unicode' \\| 'text' \\| 'auto'` | no       |\n| `contentId`         | `string`                        | no       |\n| `entityId`          | `string`                        | no       |\n| `appendToUserAgent` | `string`                        | no       |\n| `timeout`           | `number`                        | no       |\n| `apiHost`           | `string`                        | no       |\n\n\\* Authenticate with `applicationId` + `privateKey` (JWT, recommended by\nVonage) or with `apiKey` + `apiSecret` (basic auth).\n\n```ts\nclients: {\n  vonage: drivers.vonage({\n    apiKey: env.get('VONAGE_API_KEY'),\n    apiSecret: env.get('VONAGE_API_SECRET'),\n  }),\n}\n```\n\nThe webhook is mapped to `webhookUrl` and the response carries a single\n`messageUUID`. `contentId` and `entityId` satisfy the regulatory requirements\nof some countries. Errors come back as proper HTTP failures and are surfaced as\n`E_SMS_DRIVER_ERROR` with the provider's own title and detail.\n\n### Amazon SNS — `drivers.sns`\n\n| Option                              | Type                                 | Required |\n| ----------------------------------- | ------------------------------------ | -------- |\n| `key`                               | `string`                             | yes      |\n| `secret`                            | `string`                             | yes      |\n| `region`                            | `string`                             | yes      |\n| `type`                              | `'Promotional' \\| 'Transactional'`   | yes      |\n| `endpoint`                          | `string`                             | no       |\n| `monthlySpendLimit`                 | `number`                             | no       |\n| `usageReportS3Bucket`               | `string`                             | no       |\n| `deliveryStatusIAMRole`             | `string`                             | no       |\n| `deliveryStatusSuccessSamplingRate` | `number`                             | no       |\n\nSNS reports deliveries through CloudWatch, so `acceptWebhook` is `false` and the `webhook` option is not available.\n\n**Credentials come from IAM, not from the SNS console.** Create an IAM user, then\nan access key for it, and use the pair as `key` and `secret`. New AWS accounts\nstart in the *SMS sandbox*, where you may only send to phone numbers verified\nbeforehand in SNS → Text messaging (SMS).\n\nThe `type` and the sender are sent as **per message** attributes\n(`AWS.SNS.SMS.SMSType`, `AWS.SNS.SMS.SenderID`), so concurrent sends never\ninterfere with each other. Amazon only accepts a sender ID of 1 to 11\nalphanumeric characters starting with a letter — when the resolved sender is a\nphone number the attribute is omitted and SNS falls back to the originating\nnumber of the account.\n\nThe last four options in the table above have **no per message equivalent**:\nAmazon exposes them at the account level only, shared by every application\npublishing through the same AWS account. Defining any of them makes the driver\ncall `SetSMSAttributes` once per driver instance. Leave them unset and this\nminimal policy is enough:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [{ \"Effect\": \"Allow\", \"Action\": \"sns:Publish\", \"Resource\": \"*\" }]\n}\n```\n\nAdd `sns:SetSMSAttributes` to the `Action` list only if you configure the\naccount level options.\n\nPer message overrides are available at runtime:\n\n```ts\nawait sms.use('sns').send(\n  (message) => message.to('+12121212121').message('Hello world'),\n  { smsType: 'Promotional', maxPrice: 0.5 }\n)\n```\n\n### Infobip — `drivers.infobip`\n\n| Option               | Type                                              | Required |\n| -------------------- | ------------------------------------------------- | -------- |\n| `baseUrl`            | `string`                                          | yes      |\n| `apiKey`             | `string`                                          | no\\*     |\n| `username`           | `string`                                          | no\\*     |\n| `password`           | `string`                                          | no\\*     |\n| `authType`           | `'App' \\| 'Basic'`                                | no       |\n| `flash`              | `boolean`                                         | no       |\n| `intermediateReport` | `boolean`                                         | no       |\n| `webhookContentType` | `'application/json' \\| 'application/xml'`         | no       |\n| `sendingSpeedLimit`  | `{ amount, timeUnit }`                            | no       |\n\n\\* Use `apiKey` with `authType: 'App'`, or `username` + `password` with `authType: 'Basic'`. The webhook is mapped to `notifyUrl`.\n\nInfobip answers with one entry per destination, so each becomes its own `SmsResponse`. The `bulkId` of the enclosing response — the handle you need to fetch delivery reports for a batch — is copied onto every entry's `original`.\n\n## API reference\n\n### `sms` (SmsManager)\n\n| Method                              | Description                                              |\n| ----------------------------------- | -------------------------------------------------------- |\n| `use(name?)`                        | Resolve a client. Falls back to the default one           |\n| `send(callback, options?)`          | Send through the default client                           |\n| `sendLater(callback, options?)`     | Queue through the default client                          |\n| `sendMany(callback, options?)`      | Same message to many recipients                          |\n| `sendBulk(callback, options?)`      | Different message per recipient                          |\n| `setMessenger(factory)`             | Replace the background queue for every client             |\n| `config`                            | The resolved config                                      |\n\n### `sms.use(...)` (SmsClient)\n\n| Method                          | Description                                             |\n| ------------------------------- | ------------------------------------------------------- |\n| `send(callback, options?)`      | Send a message                                          |\n| `sendLater(callback, options?)` | Queue a message                                         |\n| `sendMany(callback, options?)`  | Same message to many recipients                         |\n| `sendBulk(callback, options?)`  | Different message per recipient                         |\n| `sendCompiled(compiledSms)`     | Send an already compiled message                        |\n| `setMessenger(messenger)`       | Replace the background queue for this client             |\n| `name`                          | The client name                                          |\n| `driver`                        | The underlying driver instance                          |\n\n### `SmsResponse`\n\n| Property    | Description                                                          |\n| ----------- | -------------------------------------------------------------------- |\n| `messageId` | The identifier assigned by the provider, normalized across drivers   |\n| `original`  | The raw payload the provider returned for this message               |\n| `toJSON()`  | A plain object of the two, handy for logging                         |\n\n### Errors\n\nExported under the `errors` namespace, all built with `createError`:\n\n| Error | Status | Raised when |\n| --- | --- | --- |\n| `E_INVALID_PHONE_NUMBER` | 422 | the recipient is not in the E.164 format |\n| `E_MISSING_PARAMETER` | 422 | a required field is missing on the message |\n| `E_MISSING_MESSAGE` | 422 | the message body is empty |\n| `E_INVALID_WEBHOOK_FORMAT` | 422 | the webhook is not a valid URL |\n| `E_INVALID_WEBHOOK_PROTOCOL` | 422 | the webhook is not HTTP(S) |\n| `E_SMS_DRIVER_ERROR` | 400 | the provider rejected the message |\n| `E_NO_SENDER_PROVIDED` | 500 | no sender is configured anywhere |\n\nMistakes made while composing a message are 422s, so an invalid phone number\nsurfaces as a validation error rather than an internal one. Only a broken\nconfiguration, which the caller cannot fix at runtime, stays a 500.\n\n### Debugging\n\nSet `NODE_DEBUG=adonisjs:sms` to trace client creation and message delivery.\n\n## License\n\n[MIT](LICENSE.md)\n\n[github-actions-image]: https://img.shields.io/github/actions/workflow/status/Artian-Techs/adonis-sms/test.yml?style=for-the-badge\n[github-actions-url]: https://github.com/Artian-Techs/adonis-sms/actions/workflows/test.yml 'github-actions'\n[npm-image]: https://img.shields.io/npm/v/@artian-techs/adonis-sms.svg?style=for-the-badge&logo=npm\n[npm-url]: https://npmjs.org/package/@artian-techs/adonis-sms 'npm'\n[license-image]: https://img.shields.io/npm/l/@artian-techs/adonis-sms?color=blueviolet&style=for-the-badge\n[license-url]: LICENSE.md 'license'\n[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript\n[typescript-url]: https://www.typescriptlang.org 'typescript'\n","readmeFilename":"README.md"}