{"id":50725834,"url":"https://github.com/pangerlkr/sms-api","last_synced_at":"2026-06-10T04:01:21.150Z","repository":{"id":351934474,"uuid":"1213130014","full_name":"pangerlkr/SMS-API","owner":"pangerlkr","description":"A REST API platform and CLI tool that lets users route programmatic SMS messages through their registered Indian SIM cards.","archived":false,"fork":false,"pushed_at":"2026-04-28T15:35:38.000Z","size":290,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-28T17:21:29.086Z","etag":null,"topics":["cli","express","india","nodejs","rest-api","sms-gateway","sqlite","webhooks"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pangerlkr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-17T04:27:53.000Z","updated_at":"2026-04-28T15:40:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pangerlkr/SMS-API","commit_stats":null,"previous_names":["pangerlkr/sms-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pangerlkr/SMS-API","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pangerlkr%2FSMS-API","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pangerlkr%2FSMS-API/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pangerlkr%2FSMS-API/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pangerlkr%2FSMS-API/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pangerlkr","download_url":"https://codeload.github.com/pangerlkr/SMS-API/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pangerlkr%2FSMS-API/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34136112,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","express","india","nodejs","rest-api","sms-gateway","sqlite","webhooks"],"created_at":"2026-06-10T04:01:19.925Z","updated_at":"2026-06-10T04:01:21.143Z","avatar_url":"https://github.com/pangerlkr.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SMS-API\n\n\u003e A REST API platform that lets users in India register their SIM card phone numbers as SMS senders and programmatically route messages through those SIM cards for their businesses, tools, or platforms.\n\n---\n\n## Table of Contents\n\n- [How It Works](#how-it-works)\n- [Interfaces](#interfaces)\n- [Quick Start](#quick-start)\n- [Serverless Deployment (AWS Lambda)](#serverless-deployment-aws-lambda)\n- [Architecture](#architecture)\n- [Message Flow](#message-flow)\n- [SMS Status Lifecycle](#sms-status-lifecycle)\n- [Database Schema](#database-schema)\n- [Web UI](#web-ui)\n- [CLI](#cli)\n- [API Reference](#api-reference)\n  - [Authentication](#authentication)\n  - [Auth Endpoints](#auth-endpoints)\n  - [SIM Cards](#sim-cards)\n  - [API Keys](#api-keys)\n  - [Sending SMS](#sending-sms)\n  - [Webhooks](#webhooks)\n- [Environment Variables](#environment-variables)\n- [Tech Stack](#tech-stack)\n- [Running Tests](#running-tests)\n- [Architecture Notes](#architecture-notes)\n\n---\n\n## How It Works\n\n1. **Register** – Create an account on the platform.\n2. **Add your SIM** – Register your Indian mobile number and verify ownership with a one-time password (OTP).\n3. **Generate an API key** – Create a key that your application uses to authenticate requests.\n4. **Send SMS** – Call `POST /api/sms/send` from your application. Messages are routed through your verified SIM card.\n5. **Companion Device** – Install the companion app on the phone whose SIM card you registered. The app polls or listens for webhook events and delivers messages using the device's native SMS capability. Alternatively connect a GSM modem to the webhook endpoint.\n\n---\n\n## Interfaces\n\n| Interface | Access Point | Description |\n|-----------|-------------|-------------|\n| **Web UI** | `http://localhost:3000` | Browser dashboard for managing accounts, SIM cards, API keys, and logs |\n| **CLI** | `sms-api` command | Command-line tool for scripting and terminal-based workflows |\n| **REST API** | `http://localhost:3000/api` | Programmatic HTTP access (documented below) |\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n| Requirement | Version |\n|------------|---------|\n| Node.js | 18+ |\n\n### Installation\n\n```bash\ngit clone https://github.com/pangerlkr/SMS-API.git\ncd SMS-API\nnpm install\ncp .env.example .env       # edit JWT_SECRET\nnpm start\n```\n\nThe server starts on `http://localhost:3000` by default.\n\n---\n\n## Serverless Deployment (AWS Lambda)\n\nThe platform ships with a pre-built Lambda handler and a [Serverless Framework](https://www.serverless.com/) configuration so you can deploy to AWS API Gateway + Lambda with a single command.\n\n### Prerequisites\n\n| Requirement | Notes |\n|------------|-------|\n| AWS account | IAM user with Lambda, API Gateway, CloudFormation, S3, and IAM permissions |\n| Serverless Framework v3 | `npm install -g serverless@3` |\n| Node.js 18+ | Same as above |\n\n### One-time AWS credentials setup\n\n```bash\nserverless config credentials --provider aws \\\n  --key \u003cYOUR_AWS_ACCESS_KEY_ID\u003e \\\n  --secret \u003cYOUR_AWS_SECRET_ACCESS_KEY\u003e\n```\n\n### Deploy\n\n```bash\n# Set required environment variable\nexport JWT_SECRET=$(node -e \"console.log(require('crypto').randomBytes(48).toString('hex'))\")\n\n# Deploy to Mumbai (ap-south-1) production stage\nserverless deploy --stage prod --region ap-south-1\n```\n\nServerless Framework will:\n1. Package the application (excluding dev files and test fixtures)\n2. Create an S3 bucket for deployment artifacts\n3. Deploy an AWS Lambda function and an HTTP API Gateway endpoint\n4. Output the public URL\n\n### Database persistence\n\nBy default the Lambda function uses an **in-memory SQLite database** (`DB_PATH=:memory:`).  \nAll data is lost when the Lambda container is recycled.\n\nFor a persistent database, mount an [Amazon EFS](https://aws.amazon.com/efs/) access point to the Lambda function and set `DB_PATH` to the mount path (e.g. `/mnt/efs/sms_api.db`). Refer to the [AWS docs on EFS + Lambda](https://docs.aws.amazon.com/lambda/latest/dg/configuration-filesystem.html) for the IAM and VPC setup required.\n\n### CI/CD with GitHub Actions\n\nTwo workflows are included in `.github/workflows/`:\n\n| Workflow | Trigger | What it does |\n|----------|---------|--------------|\n| `ci.yml` | Push / PR to `main` | Installs dependencies and runs the full test suite on Node 18 and 20 |\n| `deploy.yml` | Push to `main` | Runs tests, then deploys to AWS Lambda via Serverless Framework |\n\nRequired GitHub repository secrets and variables for the deploy workflow:\n\n| Name | Kind | Description |\n|------|------|-------------|\n| `AWS_ACCESS_KEY_ID` | Secret | AWS IAM access key ID |\n| `AWS_SECRET_ACCESS_KEY` | Secret | AWS IAM secret access key |\n| `JWT_SECRET` | Secret | Long random string for JWT signing |\n| `JWT_EXPIRES_IN` | Variable | Token expiry (e.g. `7d`); defaults to `7d` |\n| `CORS_ORIGIN` | Variable | Allowed origin(s); leave blank to block cross-origin in production |\n| `DB_PATH` | Variable | Database path; defaults to `:memory:` |\n\n---\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                          SMS-API Platform                        │\n│                                                                  │\n│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌─────────────┐  │\n│  │  Web UI  │   │   CLI    │   │ REST API │   │  /health    │  │\n│  │:3000     │   │ sms-api  │   │  /api/*  │   │  endpoint   │  │\n│  └────┬─────┘   └────┬─────┘   └────┬─────┘   └─────────────┘  │\n│       └──────────────┴──────────────┘                            │\n│                            │                                     │\n│                    ┌───────▼────────┐                            │\n│                    │  Express App   │                            │\n│                    │  + Helmet/CORS │                            │\n│                    │  + Rate Limit  │                            │\n│                    └───────┬────────┘                            │\n│                            │                                     │\n│          ┌─────────────────┼─────────────────┐                  │\n│          │                 │                 │                   │\n│   ┌──────▼──────┐  ┌───────▼──────┐  ┌──────▼──────┐           │\n│   │  JWT Auth   │  │  API Key     │  │  Validators │           │\n│   │  Middleware │  │  Middleware  │  │  (express-  │           │\n│   └──────┬──────┘  └───────┬──────┘  │  validator) │           │\n│          │                 │         └─────────────┘           │\n│          └─────────────────┘                                    │\n│                            │                                    │\n│                    ┌───────▼────────┐                           │\n│                    │  Controllers   │                           │\n│                    │  auth / sim /  │                           │\n│                    │  keys / sms /  │                           │\n│                    │  webhooks      │                           │\n│                    └───────┬────────┘                           │\n│                            │                                    │\n│                    ┌───────▼────────┐                           │\n│                    │  SQLite DB     │                           │\n│                    │  (sql.js /     │                           │\n│                    │   WebAssembly) │                           │\n│                    └────────────────┘                           │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## Message Flow\n\n```mermaid\nsequenceDiagram\n    participant App as Your Application\n    participant API as SMS-API Server\n    participant DB as SQLite DB\n    participant WH as Companion Device\u003cbr/\u003e(Webhook)\n    participant SIM as SIM Card / GSM Modem\n    participant RCP as Recipient\n\n    App-\u003e\u003eAPI: POST /api/sms/send\u003cbr/\u003eX-API-Key: smsapi_...\n    API-\u003e\u003eDB: Validate API key \u0026 SIM card\n    DB--\u003e\u003eAPI: Key + SIM valid\n    API-\u003e\u003eDB: Create sms_log (status: queued)\n    API-\u003e\u003eDB: Look up registered webhook for SIM\n    alt Webhook registered\n        API-\u003e\u003eWH: POST {log_id, to, message, from, ...}\u003cbr/\u003eX-SMS-API-Secret: \u003csecret\u003e\n        API-\u003e\u003eDB: Update status → dispatched\n        WH-\u003e\u003eSIM: Send native SMS\n        WH-\u003e\u003eAPI: POST /api/sms/status {status: sent}\n        API-\u003e\u003eDB: Update status → sent / delivered\n    else No webhook\n        API-\u003e\u003eDB: Update status → pending_device\n    end\n    API--\u003e\u003eApp: {log_id, status, from, to}\n    SIM--\u003e\u003eRCP: SMS delivered\n```\n\n---\n\n## SMS Status Lifecycle\n\n```mermaid\nstateDiagram-v2\n    [*] --\u003e queued : POST /api/sms/send received\n    queued --\u003e pending_device : No webhook registered\n    queued --\u003e dispatched : Webhook POST succeeded\n    pending_device --\u003e dispatched : Device connects \u0026 picks up message\n    dispatched --\u003e sent : Companion device confirms send\n    sent --\u003e delivered : Delivery receipt received\n    dispatched --\u003e failed : Webhook unreachable / device error\n    sent --\u003e failed : Carrier rejection\n    delivered --\u003e [*]\n    failed --\u003e [*]\n```\n\n| Status | Meaning |\n|--------|---------|\n| `queued` | Message received by the server, not yet dispatched |\n| `pending_device` | No webhook registered; waiting for a companion device to connect |\n| `dispatched` | Message forwarded to the companion device via webhook |\n| `sent` | Companion device confirms the SMS was sent from the SIM |\n| `delivered` | Delivery receipt received from the carrier |\n| `failed` | Delivery failed (webhook unreachable or carrier error) |\n\n---\n\n## Database Schema\n\nThe platform uses a SQLite database with five tables:\n\n### `users`\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `id` | TEXT | Primary key (UUID) |\n| `name` | TEXT | Display name |\n| `email` | TEXT | Unique, used for login |\n| `password_hash` | TEXT | bcrypt hash |\n| `created_at` | TEXT | ISO 8601 timestamp |\n\n### `sim_cards`\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `id` | TEXT | Primary key (UUID) |\n| `user_id` | TEXT | FK → `users.id` |\n| `phone_number` | TEXT | Indian mobile number (normalised) |\n| `label` | TEXT | Optional human-readable label |\n| `verified` | INTEGER | `0` = unverified, `1` = verified |\n| `otp_code` | TEXT | Pending OTP (cleared after verify) |\n| `otp_expires_at` | TEXT | OTP expiry timestamp |\n| `active` | INTEGER | `1` = active, `0` = deactivated |\n| `created_at` | TEXT | ISO 8601 timestamp |\n\n### `api_keys`\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `id` | TEXT | Primary key (UUID) |\n| `user_id` | TEXT | FK → `users.id` |\n| `key_value` | TEXT | Unique, prefixed `smsapi_…` |\n| `name` | TEXT | Human-readable label |\n| `active` | INTEGER | `1` = active, `0` = revoked |\n| `last_used_at` | TEXT | Updated on each authenticated request |\n| `created_at` | TEXT | ISO 8601 timestamp |\n\n### `sms_logs`\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `id` | TEXT | Primary key (UUID) |\n| `user_id` | TEXT | FK → `users.id` |\n| `sim_card_id` | TEXT | FK → `sim_cards.id` |\n| `api_key_id` | TEXT | FK → `api_keys.id` |\n| `to_number` | TEXT | Recipient phone number |\n| `message` | TEXT | SMS body |\n| `status` | TEXT | See [SMS Status Lifecycle](#sms-status-lifecycle) |\n| `error_message` | TEXT | Populated on failure |\n| `sent_at` | TEXT | Timestamp when `sent` status was set |\n| `created_at` | TEXT | ISO 8601 timestamp |\n\n### `webhooks`\n\n| Column | Type | Notes |\n|--------|------|-------|\n| `id` | TEXT | Primary key (UUID) |\n| `user_id` | TEXT | FK → `users.id` |\n| `sim_card_id` | TEXT | FK → `sim_cards.id` |\n| `endpoint_url` | TEXT | Companion device URL |\n| `secret` | TEXT | Shared secret sent in `X-SMS-API-Secret` header |\n| `active` | INTEGER | `1` = active, `0` = deleted |\n| `created_at` | TEXT | ISO 8601 timestamp |\n\n---\n\n## Web UI\n\nOpen `http://localhost:3000` in your browser. You can:\n\n| Tab | What you can do |\n|-----|----------------|\n| **Register / Log in** | Create an account or sign in |\n| **SIM Cards** | Register an Indian mobile number and verify it with an OTP |\n| **API Keys** | Create and revoke API keys (full key shown once at creation) |\n| **Send SMS** | Send a message using an API key and a verified SIM card |\n| **Logs** | Paginated history of all outbound messages |\n| **Webhooks** | Register companion device endpoints for message delivery |\n\n---\n\n## CLI\n\nInstall the CLI globally after running `npm install`:\n\n```bash\nnpm link          # makes 'sms-api' available system-wide\n# or run directly:\nnode src/cli/index.js --help\n```\n\nBy default the CLI talks to `http://localhost:3000`. Override with `--url` or the `$SMSAPI_URL` environment variable.\n\n### Available Commands\n\n| Command | Description |\n|---------|-------------|\n| `sms-api register` | Create a new account (interactive prompts) |\n| `sms-api login` | Log in and store credentials locally |\n| `sms-api logout` | Clear stored credentials |\n| `sms-api profile` | Show your account profile |\n| `sms-api sim list` | List registered SIM cards |\n| `sms-api sim add` | Register a new Indian SIM card |\n| `sms-api sim verify` | Verify a SIM card with its OTP |\n| `sms-api sim resend` | Resend the OTP for an unverified SIM card |\n| `sms-api sim remove \u003cid\u003e` | Deactivate a SIM card |\n| `sms-api keys list` | List API keys |\n| `sms-api keys create` | Create a new API key |\n| `sms-api keys revoke \u003cid\u003e` | Revoke an API key |\n| `sms-api sms send` | Send an SMS (uses X-API-Key) |\n| `sms-api sms logs` | View paginated SMS logs |\n| `sms-api webhooks list` | List registered webhooks |\n| `sms-api webhooks add` | Register a companion device webhook |\n| `sms-api webhooks remove \u003cid\u003e` | Delete a webhook |\n\nCredentials (JWT token) are stored in `~/.smsapi/config.json` after login. Pass all values as flags or let the CLI prompt interactively.\n\n**Example workflow:**\n\n```bash\nsms-api register --name \"Rahul\" --email rahul@example.com --password secret123\nsms-api sim add --phone +919876543210 --label \"Business\"\n# Copy the sim_card_id and otp_for_testing from the output, then:\nsms-api sim verify --id \u003csim_card_id\u003e --otp \u003cotp\u003e\nsms-api keys create --name \"My App\"\n# Copy the full key value, then:\nsms-api sms send --key smsapi_... --to 9123456789 --message \"Hello from CLI!\"\nsms-api sms logs\n```\n\n---\n\n## API Reference\n\n### Base URL\n\n```\nhttp://localhost:3000/api\n```\n\n### Authentication\n\nThe platform uses two authentication mechanisms:\n\n| Mechanism | Header | Used for |\n|-----------|--------|----------|\n| **JWT Bearer** | `Authorization: Bearer \u003ctoken\u003e` | User management – SIM cards, API keys, logs |\n| **API Key** | `X-API-Key: smsapi_...` | Sending SMS from your application |\n\n### Endpoint Summary\n\n| Method | Path | Auth | Description |\n|--------|------|------|-------------|\n| `POST` | `/api/auth/register` | None | Create a new user account |\n| `POST` | `/api/auth/login` | None | Log in and receive a JWT |\n| `GET` | `/api/auth/profile` | JWT | Fetch your profile |\n| `POST` | `/api/sim/register` | JWT | Register a new SIM card |\n| `POST` | `/api/sim/verify` | JWT | Verify SIM ownership via OTP |\n| `POST` | `/api/sim/resend` | JWT | Resend verification OTP for an unverified SIM card |\n| `GET` | `/api/sim` | JWT | List your SIM cards |\n| `DELETE` | `/api/sim/:id` | JWT | Deactivate a SIM card |\n| `POST` | `/api/keys` | JWT | Create an API key |\n| `GET` | `/api/keys` | JWT | List API keys (masked values) |\n| `DELETE` | `/api/keys/:id` | JWT | Revoke an API key |\n| `POST` | `/api/sms/send` | API Key | Send an SMS |\n| `POST` | `/api/sms/status` | API Key | Update delivery status (companion device callback) |\n| `GET` | `/api/sms/logs` | JWT | View paginated SMS logs |\n| `POST` | `/api/webhooks` | JWT | Register a companion device webhook |\n| `GET` | `/api/webhooks` | JWT | List webhooks |\n| `DELETE` | `/api/webhooks/:id` | JWT | Delete a webhook |\n| `GET` | `/health` | None | Health check |\n\n---\n\n### Auth Endpoints\n\n#### Register\n\n```\nPOST /api/auth/register\n```\n\n**Body:**\n```json\n{\n  \"name\": \"Rahul Sharma\",\n  \"email\": \"rahul@example.com\",\n  \"password\": \"securepassword123\"\n}\n```\n\n**Response `201`:**\n```json\n{\n  \"message\": \"User registered successfully\",\n  \"token\": \"\u003cjwt\u003e\",\n  \"user\": { \"id\": \"...\", \"name\": \"Rahul Sharma\", \"email\": \"rahul@example.com\" }\n}\n```\n\n#### Login\n\n```\nPOST /api/auth/login\n```\n\n**Body:**\n```json\n{ \"email\": \"rahul@example.com\", \"password\": \"securepassword123\" }\n```\n\n**Response `200`:**\n```json\n{ \"message\": \"Login successful\", \"token\": \"\u003cjwt\u003e\", \"user\": { ... } }\n```\n\n#### Get Profile\n\n```\nGET /api/auth/profile\nAuthorization: Bearer \u003cjwt\u003e\n```\n\n---\n\n### SIM Cards\n\nAll SIM card endpoints require `Authorization: Bearer \u003cjwt\u003e`.\n\n#### Register a SIM Card\n\n```\nPOST /api/sim/register\n```\n\n**Body:**\n```json\n{\n  \"phone_number\": \"+919876543210\",\n  \"label\": \"Business SIM\"\n}\n```\n\nAccepted formats for Indian numbers:\n\n| Format | Example |\n|--------|---------|\n| International | `+91XXXXXXXXXX` |\n| Without `+` | `91XXXXXXXXXX` |\n| With leading `0` | `0XXXXXXXXXX` |\n| 10-digit | `XXXXXXXXXX` |\n\n**Response `201`:**\n```json\n{\n  \"message\": \"SIM card registered. Please verify using the OTP sent to your number.\",\n  \"sim_card_id\": \"uuid\",\n  \"otp_for_testing\": \"123456\"\n}\n```\n\n\u003e **Note:** In production, the OTP is delivered via SMS to the registered number. The `otp_for_testing` field is present for development convenience and should be removed in production.\n\n#### Verify a SIM Card\n\n```\nPOST /api/sim/verify\n```\n\n**Body:**\n```json\n{ \"sim_card_id\": \"uuid\", \"otp\": \"123456\" }\n```\n\n#### Resend Verification OTP\n\n```\nPOST /api/sim/resend\n```\n\nUse this when the OTP has expired or was not received. Generates and returns a fresh 6-digit OTP for the given unverified SIM card.\n\n**Body:**\n```json\n{ \"sim_card_id\": \"uuid\" }\n```\n\n**Response `200`:**\n```json\n{\n  \"message\": \"OTP resent. Please verify your SIM card.\",\n  \"sim_card_id\": \"uuid\",\n  \"otp_for_testing\": \"654321\"\n}\n```\n\nReturns `409` if the SIM card is already verified, `404` if the SIM card is not found.\n\n#### List SIM Cards\n\n```\nGET /api/sim\n```\n\n#### Remove a SIM Card\n\n```\nDELETE /api/sim/:id\n```\n\n---\n\n### API Keys\n\nAll API key endpoints require `Authorization: Bearer \u003cjwt\u003e`.\n\n#### Create API Key\n\n```\nPOST /api/keys\n```\n\n**Body:**\n```json\n{ \"name\": \"My Business App\" }\n```\n\n**Response `201`:**\n```json\n{\n  \"message\": \"API key created\",\n  \"api_key\": {\n    \"id\": \"uuid\",\n    \"name\": \"My Business App\",\n    \"key_value\": \"smsapi_...\",\n    \"created_at\": \"...\"\n  },\n  \"warning\": \"Store this key securely. It will not be shown in full again.\"\n}\n```\n\n#### List API Keys\n\n```\nGET /api/keys\n```\n\nKey values are masked in the listing response.\n\n#### Revoke API Key\n\n```\nDELETE /api/keys/:id\n```\n\n---\n\n### Sending SMS\n\n#### Send an SMS\n\n```\nPOST /api/sms/send\nX-API-Key: smsapi_...\n```\n\n**Body:**\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `to` | string | Yes | Recipient phone number |\n| `message` | string | Yes | SMS body text |\n| `sim_card_id` | string | No | UUID of SIM to use; defaults to first verified SIM |\n\n```json\n{\n  \"to\": \"9123456789\",\n  \"message\": \"Your OTP is 456789\",\n  \"sim_card_id\": \"uuid\"\n}\n```\n\n**Response `200`:**\n```json\n{\n  \"message\": \"SMS queued. Connect a companion device or register a webhook to complete delivery.\",\n  \"log_id\": \"uuid\",\n  \"from\": \"9876543210\",\n  \"to\": \"9123456789\",\n  \"status\": \"pending_device\"\n}\n```\n\n#### Update Delivery Status (Companion Device Callback)\n\n```\nPOST /api/sms/status\nX-API-Key: smsapi_...\n```\n\n**Body:**\n```json\n{\n  \"log_id\": \"uuid\",\n  \"status\": \"sent\",\n  \"error_message\": null\n}\n```\n\n#### View SMS Logs\n\n```\nGET /api/sms/logs?page=1\u0026limit=20\nAuthorization: Bearer \u003cjwt\u003e\n```\n\n---\n\n### Webhooks\n\nRegister an HTTP endpoint on your companion device. When an SMS is sent, the platform will POST the message details to this URL so the device can deliver it.\n\nAll webhook endpoints require `Authorization: Bearer \u003cjwt\u003e`.\n\n#### Register Webhook\n\n```\nPOST /api/webhooks\n```\n\n**Body:**\n```json\n{\n  \"sim_card_id\": \"uuid\",\n  \"endpoint_url\": \"https://your-device.example.com/sms\",\n  \"secret\": \"optional-shared-secret\"\n}\n```\n\n**Webhook payload** (sent by the server to your device):\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `log_id` | string | UUID of the SMS log entry |\n| `to` | string | Recipient phone number |\n| `message` | string | SMS body |\n| `from` | string | Sender's phone number (your SIM) |\n| `webhook_id` | string | UUID of the webhook |\n| `timestamp` | string | ISO 8601 send time |\n\n```json\n{\n  \"log_id\": \"uuid\",\n  \"to\": \"9123456789\",\n  \"message\": \"Your OTP is 456789\",\n  \"from\": \"9876543210\",\n  \"webhook_id\": \"uuid\",\n  \"timestamp\": \"2024-01-01T00:00:00.000Z\"\n}\n```\n\nThe shared secret is passed in the `X-SMS-API-Secret` header so you can verify authenticity.\n\n#### List Webhooks\n\n```\nGET /api/webhooks\n```\n\n#### Delete Webhook\n\n```\nDELETE /api/webhooks/:id\n```\n\n---\n\n## Environment Variables\n\n| Variable | Default | Required | Description |\n|----------|---------|----------|-------------|\n| `PORT` | `3000` | No | HTTP port the server listens on |\n| `JWT_SECRET` | — | **Yes (prod)** | Secret used to sign JWTs; use a long random string |\n| `JWT_EXPIRES_IN` | `7d` | No | JWT expiry duration (e.g. `1h`, `7d`, `30d`) |\n| `DB_PATH` | `./data/sms_api.db` | No | Path to the SQLite database file |\n\n---\n\n## Tech Stack\n\n| Layer | Technology | Notes |\n|-------|-----------|-------|\n| Runtime | Node.js 18+ | |\n| Framework | Express 4 | REST API + static file server |\n| Security | Helmet, CORS | CSP, XSS, and CORS hardening |\n| Rate Limiting | express-rate-limit | 100 req / 15 min per IP |\n| Authentication | jsonwebtoken, bcryptjs | JWT for users, API key for sends |\n| Validation | express-validator | Request body validation |\n| Database | sql.js (SQLite/WASM) | No native build required |\n| CLI | Commander.js | `sms-api` binary |\n| Testing | Jest + Supertest | `npm test` |\n| Dev Server | Nodemon | `npm run dev` |\n\n---\n\n## Running Tests\n\n```bash\nnpm test\n```\n\n---\n\n## Architecture Notes\n\n- **Database:** SQLite via [sql.js](https://github.com/sql-js/sql.js) (pure WebAssembly – no native build required). For high-volume production deployments, swap `src/db/index.js` for a PostgreSQL/MySQL adapter.\n- **SIM Verification:** OTP is currently returned in the API response (`otp_for_testing`). In production, integrate an SMS provider (e.g. MSG91, Exotel, Textlocal) to deliver the OTP to the handset.\n- **Message Delivery:** The platform routes messages to your physical device via webhooks. The companion mobile app (Android/iOS) or a GSM modem connected to a server listens on the webhook URL and uses the SIM card to send the message natively.\n- **Rate Limiting:** 100 requests per 15 minutes per IP.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpangerlkr%2Fsms-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpangerlkr%2Fsms-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpangerlkr%2Fsms-api/lists"}