Skip to content

Changelog

Unreleased

functions deploy says when it moves a function to CLI management

The first CLI deploy of an existing function moves it to CLI management, which turns off the web editor, rollback from the dashboard and the Assistant for it. sinch functions deploy now says so before packaging. Interactively it asks to continue (default: yes), and declining deploys nothing. With --non-interactive, CI, --json or no terminal, it prints the notice and deploys; --json includes it in warnings.

  • No notice for a new function, a function already managed from the CLI, or a deploy with SINCH_CLIENT=ai-builder.
  • A function deployed from the CLI before this change may show the notice once more.
  • Warnings about the recorded function and the notice are no longer dropped from --json warnings when validation runs.

functions versions and functions rollback

sinch functions versions lists a function's versions, and sinch functions rollback <version> restores an earlier version's code as a new version. <version> is the number versions shows or the version's id.

  • Rollback restores code only, not environment variables or database data. It confirms first when it can prompt, waits for the build (--no-wait skips that), and refuses a version whose build failed with exit code 103.
  • Rollback doesn't change sinch.json. Run sinch functions download to pull the restored code; a deploy from the old copy is refused as stale until you do.
  • Errors from the API now show the API's detail in the CLI's message.

functions deploy updates against the version your copy came from

sinch functions deploy now updates the function recorded in sinch.json by its id, and sends the recorded versionId as the base version. If the function changed since your copy was taken, deploy asks before overwriting it, or exits with code 103 when it can't ask (--non-interactive, CI, --json). A copied folder deployed under a new name creates that function instead of touching the original, with a warning that the recorded function wasn't used.

  • --overwrite deploys without the base-version check. --force does not skip it.
  • --message <text> (-m) saves a note on the new version.
  • Deploy records versionId in sinch.json, and includes it in --json output.
  • A deploy under a different --name no longer overwrites functionId in sinch.json. Before, a preview deploy moved the project to the preview function.

sinch functions download records the live version's versionId in sinch.json, and fails without leaving files behind when the function's configuration can't be fetched. sinch functions list shows a Display Name column and sinch functions status prints the display name.

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.

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). 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]'.

CommandBeforeNow
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 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.