# Changelog

## 0.8.2

Released 2026-10-19.

### `functions deploy` sends values with credential names as secrets

`sinch functions deploy` now sends a filled `.env` or `appsettings.json` value as a secret when its name looks like a credential. Before, every filled value was sent as plain configuration and showed in the dashboard. The function reads both kinds from its environment as before, and deploy's list of variables marks these `(secret)`. See [Configuration and secrets](/docs/functions/functions/concepts/configuration-secrets).

A name looks like a credential when its last word is `KEY`, `KEYS`, `APIKEY`, `SECRET`, `SECRETS`, `TOKEN`, `PASSWORD`, `PASSWD`, `PASS`, `PWD`, `PASSPHRASE`, `CREDENTIAL` or `CREDENTIALS`, and none of its words is `PUBLIC` or `PUBLISHABLE`. Names are split into words on non-alphanumeric characters and where a lowercase letter is followed by an uppercase one. An all-caps run is not split.

- Sent as secrets: `OPENAI_API_KEY`, `GITHUB_TOKEN`, `SMTP_PASS`.
- Kept plain: `MAX_TOKENS`, `TOKEN_URL`, `AWS_ACCESS_KEY_ID`, `PUBLIC_KEY`, `OpenAIKey`, and a filled `PROJECT_ID_API_KEY` or `VOICE_APPLICATION_KEY`.


For C#, only top-level `appsettings.json` keys count. Because the file also ships inside the package, deploy warns with the key names (not under `--json`) and points you to `dotnet user-secrets`.

A value that has deployed as a secret stays a secret, so renaming the key is the only way to make it plain again. `sinch functions download` treats it like any other secret: it is blank in `.env`, with an offer to fetch its value into your keychain. If you decline, or the run cannot prompt, a later deploy from that folder warns and leaves the key out of the function.

### The developer skills offer can install for one project

The offer to install the Sinch developer skills now asks **This project only** (followed by the folder's path), **Globally (all projects)** or **Skip**, instead of yes or no for a global install.

- After `sinch auth login`, the project choice shows only when the current folder has a `sinch.json`.
- `sinch functions init` and `sinch functions download` make the offer at the end, for the folder they created.
- **Skip** is saved as `skipSkillsPrompt` in `~/.sinch/config.json` and stops the offer everywhere. `sinch skills install` still installs the skills later.
- `--json`, `--non-interactive` and runs with no terminal never ask.


A project install writes `.agents`, `.claude` and `skills-lock.json`, so `sinch functions deploy` now leaves files and folders with those names out of the package, anywhere in the function folder.

### `functions logs -n` limits the number of entries

`sinch functions logs -n <N>` now returns at most N entries (the API caps it at 500). Before, the CLI sent a parameter the API never read, so `-n` had no effect. Without `-n` the API still returns up to 100 entries, and the help text now says 100 (it said 50). `-n` now rejects anything that is not a positive whole number, with exit 64. With `--follow` it is still checked but has no effect.

### Commands no longer keep spinning after a login retry

After a login retry, `sinch functions list`, `download <function-id>`, `delete`, `logs`, `storage` and `db`, and `sinch templates list` and `show`, now exit when the retried command finishes. Before, if you ran one of them logged out (or with stored credentials that no longer work) and logged in at the prompt, it did its work and then kept spinning until Ctrl+C. `logs --follow` still streams until Ctrl+C.

### Other changes

- `sinch functions docs --update` no longer fails every time with a `.find is not a function` error. It looks up the function named after the current folder on every page of the function list.
- Download errors from `sinch functions download`, `storage download` and `db download`, and from the template download in `sinch functions init`, now show the API's message when the error has one, instead of `Request failed with status code N`. Exit codes and message prefixes are unchanged.


## 0.8.1

Released 2026-10-08.

### `functions init` defaults to a new voice service and records what it created

The interactive voice picker now offers "Create a new one" first, and it is the default whenever the project has services; picking an existing service is opt-in. Non-interactive runs are unchanged.

- A service or number that init creates is saved in `sinch.json` as `createdResources`, with the name of the function init created it for. A picked service, or one passed with `--voice-service-id`, is never recorded.
- A `sinch functions deploy` of that function sends them, so deleting the function releases them. Deploy removes each one from `sinch.json` once the API reports it as recorded for the function.
- A deploy under another name (`--name`) does not send them. If the API does not record them, they stay in `sinch.json` and the next deploy sends them again.


If the API rejects a deploy because another function already records one of these resources as created, the error names that function and says to remove `createdResources` from `sinch.json`. A failed deploy now prints "Failed to deploy function:" once instead of twice.

### The CLI joins an inherited trace

When `TRACEPARENT` (W3C trace context, version 00) is set, every Functions API request forwards it unchanged, together with `TRACESTATE`, and the run's telemetry uses that trace id. `SINCH_CLIENT` and `SINCH_BUILDER_SESSION_ID` are sent as the `X-Sinch-Client` and `X-Sinch-Builder-Session` headers; they label diagnostics only, and invalid values are dropped. AI Builder sets these so each deploy is filed under its builder session. Without them the CLI behaves as before.

## 0.8.0

Released 2026-10-05.

### Connection settings come from your profile only

**Breaking for scripts:** a project's `sinch.json` can no longer set `projectId`, `apiUrl`, `oauthUrl`, `keyId`, or any credential or `oauth*` field; those values are ignored. If a folder's `sinch.json` names a different project than your active profile, commands now use the profile's project. To target another project, run `sinch config profile use <name>` (only `auth login` and `config get|set|list` honor the `--profile` flag; see [Configuration](/docs/functions/cli/configuration#config-profiles)). In CI, set `SINCH_KEY_ID`, `SINCH_KEY_SECRET` and `SINCH_PROJECT_ID` together. `functions download` no longer writes `projectId`. Folders downloaded earlier still work when your active profile is on the project they came from.

## 0.7.0

Released 2026-09-30.

### `functions init` can create a voice service and rent a number, and prints JSON

Voice templates now offer to create a Voice v2 service and, after showing the price and asking, rent a US LOCAL number linked to it. `VOICE_SERVICE_ID` and `VOICE_FROM_NUMBER` go into `.env`. New flags: `--create-voice-service`, `--rent-number <region>`, `--area-code <digits>`, `--force` (required to rent when non-interactive) and `--json`. New voice projects get `accessKey: "managed"` in `sinch.json`.

### `--json` on more commands

These commands now accept `--json`. Each prints one JSON document on stdout and never prompts. Where confirmation is needed and neither `--force` nor `--non-interactive` is given, `--json` fails with an error document naming `--force` and exits 66. `voice v1 conferences kick-all` (which also takes `--yes`), `functions download` (to overwrite), `storage delete`, the `sinch sip` delete commands, `auth logout` and `config profile delete` take only `--force` as confirmation.

- `sinch functions delete`, `download`, `validate`, `db download|upload`, `storage list|upload|download|delete`
- `sinch conversation` delete and stop commands (apps, contacts, conversations, messages, templates, webhooks), and `sinch sip` delete commands (acls, ACL IP ranges, endpoints, trunks) and `sip trunks acls remove`. `conversation conversations stop` and `conversation messages delete` gain `--force`, and a confirmation step they did not have before (see below).
- `sinch config` (all subcommands; `config profile delete` gains `--force`), `sinch secrets add|get|delete|clear` (`secrets list` already had `--json`), `sinch skills`, `sinch env init`
- `sinch auth status|login|logout`, `sinch health`. `auth login` needs `--credentials-file` under `--json`, and `auth logout` gains `--force`.
- `sinch numbers order resume`


`sinch functions logs` (one JSON object per line with `--follow`), `sinch fax download` and `sinch upgrade` also gain `--json`. `sinch voice v1 conferences kick-all` already accepted `--json`. It gains `-f, --force`, and `--yes` still works.

Under `--json`, missing or rejected credentials now print `{ "error": "..." }` on stdout and exit 100 or 101, with no login prompt.

### Some deletes with no terminal now exit 66

**Breaking for scripts:** with no terminal and no `-f, --force`, the `sinch sip` delete commands (acls, ACL IP ranges, endpoints, trunks) and `sip trunks acls remove` now exit 66 and change nothing. Before, they printed "Deletion cancelled" or "Operation cancelled" and exited 0.

`conversation conversations stop` and `conversation messages delete` now ask for confirmation. With no terminal and neither `-f, --force` nor `--non-interactive`, they exit 66 and do nothing. Before, they ran without asking.

### `sinch auth status` exits non-zero when no credentials are configured

**Breaking for scripts:** `sinch auth status` now exits 101 (`AUTH_FAILED`) when no credentials are configured, with and without `--json`. It used to exit 0. A script or CI step that ran it and treated exit 0 as success now sees a failure. With `--json` it prints `{ "authenticated": false }`.

Credentials that exist but are rejected, for example a wrong `SINCH_KEY_SECRET`, still exit 0. With `--json` the output has `"authenticated": true` and `"tokenValid": false`, so a CI check should read `tokenValid` rather than the exit code alone.

### `--json` list output is an object with a named array key

**Breaking for scripts:** every `--json` list is now an object with one named array key, which leaves room for pagination fields later. A script that indexed the array directly reads the key instead, for example `jq '.[0]'` becomes `jq '.faxes[0]'`.

| Command | Before | Now |
|  --- | --- | --- |
| `sinch fax list --json` | `[...]` | `{ "faxes": [...] }` |
| `sinch fax services list --json` | `[...]` | `{ "services": [...] }` |
| `sinch fax services numbers <service-id> --json` | `[...]` | `{ "numbers": [...] }` |
| `sinch fax emails list --json` | `[...]` | `{ "emails": [...] }` |
| `sinch fax emails numbers <email> --json` | `[...]` | `{ "numbers": [...] }` |
| `sinch fax cover-pages list --json` | `[...]` | `{ "coverPages": [...] }` |
| `sinch fax send --json` (with and without `--wait`) | `[...]` | `{ "faxes": [...] }` |
| `sinch numbers active list --json` | `[...]` | `{ "activeNumbers": [...] }` |
| `sinch numbers order list --json` | `[...]`, or text if empty | `{ "orders": [...] }` |
| `sinch voice calls list --json` | `[...]` | `{ "calls": [...] }` |
| `sinch voice services list --json` | `[...]` | `{ "services": [...] }` |


Commands that already printed an object (conversation, sip, porting, templates, secrets, functions list) are unchanged.

### Other changes

- A failed API call exits with a code that follows its HTTP status: 401 exits 100, 403 exits 77, 404 exits 102, 409 exits 103, and 429 or 5xx exits 69. [CI/CD](/assets/ci-cd.66afb53ca35033512942b50d6ad0ff9e0768727ac2c2100f305647ab37d7185b.8a245974.md) lists every code.
- `-a, --app-key` works on every Voice v1 command. On `voice v1 applications numbers list` it filters the listed numbers instead. `voice calls create` and `voice calls bridge` gain `--service-id`.
- `functions init` removes the half-created directory when stopped with Ctrl+C, and exits 130.
- New `sinch conversation apps update` command. It sends only the fields you pass.
- `sinch conversation apps create` no longer fails with HTTP 400 because it sent an empty body.
- `sinch functions dev` points the Voice and Conversation webhooks back at their deployed targets when it stops.