# Sinch CLI

> **Official command-line interface for Sinch - Manage Functions, Voice, SMS, and more from your terminal**

[![npm version](https://img.shields.io/npm/v/@sinch/cli.svg)](https://www.npmjs.com/package/@sinch/cli)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Overview

The Sinch CLI is your unified developer tool for managing all Sinch products. Currently featuring full support for **Sinch Functions** - create, test, and deploy serverless voice applications. More Sinch products coming soon!

## Installation

```bash
# Install
npm install -g @sinch/cli
```

**Installation Channels:**

- **`@latest`** (default) - Production-ready stable releases published to npmjs
- **`@dev`** - Cutting-edge builds auto-published to the [GitLab project registry](https://gitlab.com/sinch/sinch-projects/voice/functions/sinch-cli/-/packages) on every push. Dev builds declare the platform packages as regular `dependencies` (all 5 install) because GitLab's registry strips `optionalDependencies` from npm metadata — the launcher still picks the host's binary

**How it works — per-architecture packages:**

`@sinch/cli` is a thin launcher. The actual binary ships in a platform-specific package that npm selects automatically at install time:

| Package                   | Platform                        |
| ------------------------- | ------------------------------- |
| `@sinch/cli-linux-x64`    | Linux x64                       |
| `@sinch/cli-linux-arm64`  | Linux ARM64                     |
| `@sinch/cli-darwin-x64`   | macOS Intel                     |
| `@sinch/cli-darwin-arm64` | macOS Apple Silicon             |
| `@sinch/cli-win32-x64`    | Windows x64 / ARM64 (emulation) |

This is the same model used by esbuild and Biome. No install scripts run — `npm install --ignore-scripts` works fine. Do **not** use `--omit=optional`; the platform binary is an optional dependency and the CLI will print an actionable error if it is missing.

## Prerequisites

- **Node.js**: 20.0.0 or higher (Node 20 LTS or Node 22 LTS recommended)
- **npm**: 9.0.0 or higher
- **OS**: Windows 10+, macOS 10.15+, or Ubuntu 20.04+

## Quick Start

### 1. Authenticate with Sinch

```bash
sinch auth login
```

### 2. Create a new function

```bash
# Interactive mode - choose from templates
sinch functions init

# Or name a template directly
sinch functions init simple-voice-ivr
```

A voice template declares `VOICE_SERVICE_ID`, so `init` lists the Voice services in
your project and writes the one you pick to `.env`. That service is what routes
inbound calls to the function, and `sinch functions deploy` points its webhook at
the deployed URL. Leave the answer blank to continue without one and set the id
later.

`--non-interactive` never lists services, so pass `--voice-service-id <id>` to
route a voice function created that way; without it, init omits the key and
warns instead of writing it blank.

### 3. Test locally

```bash
cd my-voice-app
sinch functions dev

# With runtime tunnel for webhooks
sinch functions dev
```

### 4. Deploy to production

```bash
sinch functions deploy
```

### 5. Monitor your function

```bash
# List all functions
sinch functions list

# Stream logs
sinch functions logs <function-id> --follow

# Check status
sinch functions status <function-id>
```

## Available Commands

| Command                                | Description                       |
| -------------------------------------- | --------------------------------- |
| `sinch auth login`                     | Authenticate with Sinch platform  |
| `sinch functions init`                 | Create new function from template |
| `sinch functions dev`                  | Start local development server    |
| `sinch functions deploy`               | Deploy function to production     |
| `sinch functions list`                 | List all deployed functions       |
| `sinch functions logs`                 | Stream function logs              |
| `sinch functions delete`               | Delete a deployed function        |
| `sinch sip trunks list`                | List SIP trunks                   |
| `sinch sip trunks create`              | Create a new SIP trunk            |
| `sinch sip endpoints list <trunkId>`   | List endpoints for a trunk        |
| `sinch sip endpoints create <trunkId>` | Create a SIP endpoint             |
| `sinch voice calls list`               | List calls (Voice API v2)         |
| `sinch voice calls create <to>`        | Place a call (Voice API v2)       |
| `sinch voice calls patch <target>`     | Add a party to a **live** call    |
| `sinch voice services list`            | List services (Voice API v2)      |
| `sinch voice services get <id>`        | Show one service and its webhook  |
| `sinch voice v1 callouts tts <to>`     | Place a v1 TTS callout            |
| `sinch voice v1 ...`                   | Legacy v1 tree (see below)        |
| `sinch secrets add`                    | Store secrets securely            |
| `sinch config profile list`            | List credential profiles          |
| `sinch config profile create <name>`   | Create a credential profile       |
| `sinch templates list`                 | Browse available templates        |
| `sinch help`                           | Show help for any command         |

**Global options:** `--profile <name>` — use a specific credential profile for any command.

## Templates

Create functions from pre-built templates:

### Voice Templates

- `node/simple-voice-ivr` - Interactive voice menu system
- `node/call-forwarding` - Intelligent call routing
- `node/number-masking` - Privacy-preserving calls
- `node/conference-call` - Multi-party conferences
- `node/voicemail` - Complete voicemail system

### More Languages

- `csharp/simple-voice-ivr` - IVR in C#/.NET
- `java/simple-voice-ivr` - Q4 2025

## Features

### 🔐 Secure Secrets Management

Secrets are stored in your OS keychain (Windows Credential Manager, macOS Keychain, Linux Secret Service):

```bash
# Add a secret
sinch secrets add STRIPE_KEY sk_test_123456

# List secrets
sinch secrets list

# Use in your function
const stripeKey = process.env.STRIPE_KEY;
```

### 🔄 Hot Reload Development

Changes to your function are automatically reloaded during `sinch functions dev` via file watching (Node.js) or dotnet watch (C#). No flags needed:

```bash
sinch functions dev
```

### 🐛 VS Code Debugging

All templates include VS Code launch configs. Just press F5 to debug!

### 🌐 Tunnel Support

Test webhooks locally with runtime-integrated tunneling:

```bash
sinch functions dev
# Choose tunnel preference when prompted
# Tunnel runs inside your function runtime
```

### 📞 Elastic SIP Trunking

Manage SIP trunks and endpoints for voice connectivity:

```bash
# List your SIP trunks
sinch sip trunks list

# Create a new trunk (interactive)
sinch sip trunks create

# Add an endpoint to a trunk
sinch sip endpoints create <trunk-id>

# Get JSON output for scripting
sinch sip trunks list --json
```

### ☎️ Voice

`sinch voice` is the Voice API v2 surface. A v2 **service** — the container that
routes inbound calls — replaces the v1 voice application, a call is an ordered
list of commands, and authentication uses the project key pair the CLI already
stores (the `PROJECT_ID_API_KEY` / `PROJECT_ID_API_SECRET` pair in `.env`), not
the v1 application key and secret. The v1 surface (applications, callouts,
calls, conferences) is still available under `sinch voice v1`.

Voice templates published by the template registry are Voice v2 and route
through `VOICE_SERVICE_ID`, so `sinch functions init` runs the Voice service
picker for every voice template.

Voice v2 signs webhooks with `Authorization: service <serviceId>:<HMAC-SHA256>`
plus an `x-timestamp` header. The runtime verifies the signature once
`VOICE_SERVICE_SECRET` is set; until then it allows the callback through with a
warning, because Sinch does not yet expose the per-service secret.

```bash
# Services: the inbound routing container
sinch voice services list
sinch voice services get <service-id>
sinch voice services create --name "Support line" --webhook-url https://fn.example.com
sinch voice services update <service-id> --webhook-url https://fn.example.com
sinch voice services delete <service-id>

# Calls
sinch voice calls list --call-result COMPLETED
sinch voice calls get <call-id>

# Place a call
sinch voice calls create +15551234567 --from +15559876543

# Bridge a WebSocket audio leg (STREAM)
sinch voice calls create +15551234567 --stream wss://example.com/audio

# Bridge the Voice Relay — Sinch runs STT/TTS, you exchange text
sinch voice calls create +15551234567 --relay wss://example.com/relay --tts-voice Emma

# Print the request body without placing a call
sinch voice calls create +15551234567 --stream wss://example.com/audio --dry-run

# Call two parties and bridge them
sinch voice calls bridge +15551110000 +15552220000 --from +15559876543

# Control a live call: end legs, or dial a third party into its bridge
sinch voice calls hangup <target> --call-name origin
sinch voice calls patch <target> --call-name origin --dial +15550001111 --end-leg origin
```

#### Legacy v1 commands

The v1 tree stays available under `sinch voice v1` for accounts still on a voice
application:

```bash
sinch voice v1 applications callbacks set <url>
sinch voice v1 applications numbers list
sinch voice v1 callouts tts <destination> <text>
sinch voice v1 calls hangup <call-id>
sinch voice v1 conferences get <conference-id>
```

`calls patch --dial` is the one operation v1 has no equivalent of. v1 can end a
leg and play audio into one, but it cannot add a leg to a call that is already
up. v2 can, and everything already on the bridge — the other party, a media
stream, the Voice Relay — stays attached through the handover.

`sinch functions deploy` already points a service's webhook at the deployed
function when the function declares `VOICE_SERVICE_ID`; these commands are the
same surface for use outside a deploy.

#### SIP addressing

**Write v2 SIP destinations as a full URI, with the scheme.** The v2 `sip.endpoint`
schema constrains the field with `pattern: "^sips?:"`, so `sip:agent@pbx.example.com`
is required and a bare `agent@pbx.example.com` is rejected.

The scheme also picks the transport when you do not name one:

| You write                    | Transport | Meaning              |
| ---------------------------- | --------- | -------------------- |
| `sip:agent@pbx.example.com`  | `UDP`     | Unencrypted          |
| `sips:agent@pbx.example.com` | `TLS`     | Encrypted signalling |

So the one character between `sip:` and `sips:` decides whether SIP signalling is
encrypted — worth a second look before a production run.

> **Do not carry the v1 habit over.** `sinch voice v1` wants a _bare_ address and fails
> a prefixed one with `CONGESTION`; **v2** wants the prefix and rejects a bare one.
> Opposite rules, different schemas. `--dry-run` prints the exact body if you want
> to confirm which shape you are sending.

## Documentation

See the [wiki](https://gitlab.com/sinch/sinch-projects/voice/functions/sinch-cli/-/wikis/home) for architecture, features, and operations docs.

To clone locally: `git clone git@gitlab.com:sinch/sinch-projects/voice/functions/sinch-cli.wiki.git wiki`

- **Full Documentation**: [/docs/README.md](./docs/README.md)
- **Command Reference**: [/docs/COMMANDS.md](./docs/COMMANDS.md)
- **Development Guide**: [/docs/DEVELOPMENT.md](./docs/DEVELOPMENT.md)
- **Examples**: [/docs/examples/](./docs/examples/)

## Resources

- **Sinch Voice API**: https://www.sinch.com/products/apis/voice/
- **Documentation**: Full CLI reference in [/docs](./docs/README.md)
- **Templates Gallery**: Browse available templates with `sinch templates list`

## Troubleshooting

### Authentication Issues

```bash
# Check authentication status
sinch auth status

# Re-authenticate if needed
sinch auth logout
sinch auth login
```

### Debug Levels

```bash
DEBUG=1 sinch functions dev       # Basic debug output
DEBUG=2 sinch functions deploy    # Verbose + HTTP logging
DEBUG=3 sinch functions dev       # Full debug with headers
```

### Common Issues

- **Command not found**: Reinstall globally with `npm install -g @sinch/cli`
- **"platform package not installed" error**: Your package manager skipped optional dependencies, or a self-update did not complete. Reinstall without `--omit=optional`: `npm install -g @sinch/cli`. `sinch upgrade --status` shows what the CLI last tried, and `~/.sinch/update.log` has the install output
- **Authentication failed**: Check your Sinch API credentials with `sinch auth status`
- **Deployment failed**: Verify your function configuration and project access

## Development

See [docs/DEVELOPMENT.md](./docs/DEVELOPMENT.md) for the full contributor guide.

**Quick start:**

```bash
bun install --frozen-lockfile   # bun.lock is the only lockfile — don't npm install
npm run build
npm run setup   # copies binary to ~/.sinch/bin/ and installs completions
```

**Escape hatch — force a specific binary:**

`SINCH_CLI_BINARY=/path/to/binary sinch <cmd>` bypasses platform-package resolution and runs the given binary directly. Useful when testing a locally built binary or a downloaded release without installing the npm package.

## Support

- **Documentation**: [Full CLI Documentation](./docs/README.md)
- **Issues**: [Report a Bug](https://gitlab.com/sinch/sinch-projects/voice/functions/sinch-cli/-/issues)
- **Email**: support@sinch.com

## License

MIT License - Copyright © 2025 Sinch
