{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/functions/cli/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["partial"]},"type":"markdown"},"seo":{"title":"CI/CD | Sinch CLI | Sinch","siteUrl":"https://developers.sinch.com","llmstxt":{"title":"Sinch Developer Documentation","description":"LLMs.txt containing a map of all the documentation files for Sinch.","sections":[{"title":"Numbers API","description":"The Numbers API enables you to search for, view, and activate numbers. It's considered a precursor to other APIs in the Sinch product family. The numbers API can be used in tandem with any of our APIs that perform messaging or calling.","includeFiles":["docs/numbers/**/*.md","docs/numbers/**/*.yaml"],"excludeFiles":["docs/numbers/index.md"]},{"title":"Conversation API","description":"Send and receive messages globally on SMS, RCS, WhatsApp, and other channels with Sinch's Conversation API. Conversation API is the API to use for all new SMS integrations with Sinch; its SMS channel replaces the standalone SMS REST API, which reached end of sale on 15 April 2026. Conversation API has unified error messages, consistent request payloads, and channel-agnostic webhook payloads.","includeFiles":["docs/conversation/**/*.md","docs/conversation/**/*.yaml"],"excludeFiles":["docs/conversation/index.md"]},{"title":"Standalone SMS REST API (end of sale, existing integrations only)","description":"The standalone SMS REST API reached end of sale on 15 April 2026 and is no longer available to new customers. All new SMS integrations must use the SMS channel of Conversation API (https://developers.sinch.com/docs/conversation/channel-support/sms/). Use these pages only to maintain an existing standalone SMS REST API integration, or to migrate it to Conversation API.","includeFiles":["docs/sms/**/*.md","docs/sms/**/*.yaml"],"excludeFiles":["docs/sms/index.md","docs/sms/**/md_snippets/**"]},{"title":"Voice API (legacy/GA)","description":"Which Voice API? If you are building something using voice AI agents, use the Voice API v2. To learn more, you can read [Why migrate to v2?](/docs/voice-2.0/migration.md) or [What's new?](/docs/voice-2.0/whatsnew.md).\n\nThe Voice API works as a big telephony switch. The Voice API handles incoming phone calls (also known as incoming call “legs”), sets up outgoing phone calls (or outgoing call “legs”), and bridges the two. The incoming call leg may come in over a data connection (from a smartphone or web application using the Sinch SDKs) or through a local phone number (from the PSTN network). Similarly, the outgoing call leg can be over data (to another smartphone or web application using the Sinch SDKs) or the PSTN network.","includeFiles":["docs/voice/**/*.md"],"excludeFiles":["docs/voice/index.md"]},{"title":"Voice API v2 (recommended for new builds/preview)","description":"Which Voice API? If you are building something using voice AI agents, use the Voice API v2. To learn more, you can read [Why migrate to v2?](/docs/voice-2.0/migration.md) or [What's new?](/docs/voice-2.0/whatsnew.md).\n\nThe Voice API v2 enables embedding voice calling into applications for virtually limitless use cases. From voice-enabling AI agents to allow real-time conversational AI workflows over the phone, to number masking for privacy, automated appointment reminders, and outreach campaigns with answering machine detection, to bridging calls between PSTN and SIP endpoints, the API offers the flexibility and control needed for modern communications. The Sinch Programmable Voice is a cloud-native global voice platform, composed by multiple Inbound and Outbound voice channels. All these channels can be combined using our powerful markup language called SVAML v2, creating a toolbox capable of building almost limitless use cases. Our SVAML uses the concept of commands, and customers can use a combination of our commands to interact with calls in all channels, in order to model from the most basic to highly complex scenarios, required to fulfill their voice calling needs.","includeFiles":["docs/voice-2.0/**/*.md"],"excludeFiles":["docs/voice-2.0/index.md"]},{"title":"Verification API","description":"The Verification API is a platform for phone number verification. It consists of the API and different software development kits (the Sinch SDKs) that you integrate with your smartphone or web application and cloud based back-end services. Together they enable SMS, Flashcall, Phone Call and Data verification in your application.","includeFiles":["docs/verification/**/*.md","docs/verification/**/*.yaml"],"excludeFiles":["docs/verification/index.md"]},{"title":"Provisioning API","description":"Provisioning API allows you to programmatically set up your senders, accounts and templates on your favorite messaging platforms on the Conversation API. For now, you can create your first WhatsApp channel through Meta's Embedded sign up, you can configure your first SMS App and configure your webhooks. As development continues, we will be adding the most commonly used channels.","includeFiles":["docs/provisioning-api/**/*.md","docs/provisioning-api/**/*.json"],"excludeFiles":["docs/provisioning-api/index.md"]},{"title":"Elastic SIP Trunking API","description":"With Elastic SIP Trunking you can create and manage your SIP trunks and phone numbers programmatically.","includeFiles":["docs/est/**/*.md","docs/est/**/*.yaml"],"excludeFiles":["docs/est/index.md"]},{"title":"Fax API","description":"Send and receive HIPAA compliant faxes on our modern fax platform using our developer-friendly API.","includeFiles":["docs/fax/**/*.md","docs/fax/**/*.yaml"],"excludeFiles":["docs/fax/index.md"]},{"title":"In-app Voice and Video SDK","description":"The In-app Voice and Video SDK enables you to add voice and video calling capabilities directly into your mobile or web application using the Sinch SDKs.","includeFiles":["docs/in-app-calling/**/*.md"],"excludeFiles":["docs/in-app-calling/index.md"]},{"title":"Number Lookup API","description":"The Number Lookup API is designed to provide in-depth information about phone numbers, helping enterprises enhance their communication strategies and prevent fraud. By identifying the type of phone line (for example, mobile, landline, VoIP) and the associated carrier, the API allows businesses to optimize routing, reduce unnecessary costs, and improve customer engagement.","includeFiles":["docs/number-lookup-api-v2/**/*.md","docs/number-lookup-api-v2/**/*.yaml"],"excludeFiles":["docs/number-lookup-api-v2/index.md"]},{"title":"Functions","description":"Serverless compute for voice and messaging. Deploy your code and Sinch routes live calls and messages to it — no infrastructure to run.","includeFiles":["docs/functions/functions/**/*.md","docs/functions/concepts/**/*.md","docs/functions/ai-support.md","docs/functions/reference/**/*.md"],"excludeFiles":["docs/functions/functions/index.md"]},{"title":"CLI","description":"One command line for Sinch numbers, messaging, voice, fax, SIP trunking and porting, and the full Functions lifecycle — scaffold, run locally, deploy, and manage.","includeFiles":["docs/functions/cli/**/*.md"],"excludeFiles":["docs/functions/cli/index.md"]}]},"description":"Run the Sinch CLI in pipelines: environment variable authentication, --non-interactive, --json and exit codes."},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"cicd","__idx":0},"children":["CI/CD"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Running the CLI from a pipeline. The pieces you need are environment-variable"," ","authentication, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--non-interactive"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]},", and exit codes."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authentication","__idx":1},"children":["Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch auth login"]}," prompts for a project ID, key ID and key secret, then writes"," ","the secret to the OS keychain. Neither works on a runner with no stdin and no"," ","keychain. Instead, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_PROJECT_ID"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_ID"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_SECRET"]},","," ","described in"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/functions/cli/configuration#environment-variable-override"},"children":["Configuration → Environment variable override"]},","," ","and the CLI skips both the keychain and the login step entirely. All three values"," ","are in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://dashboard.sinch.com"},"children":["Sinch Dashboard"]},", under your project's"," ","Access Keys section."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Of the three, only ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_SECRET"]}," is sensitive: mask it. The other two are"," ","identifiers. Storing them as secrets too, as the GitHub Actions example below"," ","does, is fine."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"before-the-first-deploy","__idx":2},"children":["Before the first deploy"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Deploy builds a Node.js function's configuration from its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," file."," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions init"]}," adds ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".gitignore"]},", so a fresh checkout on a"," ","runner has none, and deploy fails validation with exit 78 (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MISSING_ENV_FILE"]},")."," ","Committing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," is not the fix: deploy warns when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," is tracked in git"," ","(",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ENV_IN_GIT"]},")."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Instead, commit a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env.example"]}," next to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," that lists the same keys"," ","as your local ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]},", with secret values left blank (see"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#secrets-a-function-needs-at-runtime"},"children":["Secrets a function needs at runtime"]},")."," ","Then create ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," from it in the job, before deploying:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"cp .env.example .env\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both pipeline examples below include this step."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"deploying-a-function","__idx":3},"children":["Deploying a function"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every command works from the function's own directory. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["deploy"]}," reads"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," out of the working directory and takes the name from it, so the job"," ","only has to be in the right folder:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"cd my-function\nsinch functions deploy --non-interactive\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--name"]}," flag, but pass it only when you deliberately want to deploy"," ","under a different name than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," says. In a pipeline it is a liability:"," ","rename the function in the repo and the job keeps deploying to the old name"," ","without complaining."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--non-interactive"]}," answers prompts with their defaults rather than blocking on"," ","stdin, which a runner does not have. For ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["deploy"]},", a set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]}," environment"," ","variable has the same effect, so on GitHub Actions and GitLab CI, which both set"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]},", deploy runs non-interactively even without the flag."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["One deploy prompt does not take its default. Deploy asks \"Use a managed key for"," ","this function?\", which defaults to yes, but a non-interactive run picks"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["profile"]}," instead of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["managed"]},". To use a managed key, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accessKey"]}," to"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"managed\""]}," in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," or pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--managed-keys"]},". The"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/functions/cli/commands/functions#sinch-functions-deploy"},"children":["functions command reference"]}," ","covers how deploy picks the access key."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Without ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--non-interactive"]},", commands still do not hang. When there is no terminal, a yes/no question"," ","declines, a question with a default takes it, and a question with no answerable"," ","default fails naming itself. Declining is deliberate: it means an unattended run"," ","never approves a billable or irreversible action that a person was supposed to"," ","approve. Pass the flag that authorises the action instead."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The one place that matters for deploy is validation warnings. A non-interactive"," ","deploy, which includes any deploy where ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]}," is set, goes ahead despite them."," ","With ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]}," unset, no terminal and neither ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--force"]}," nor ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--non-interactive"]},","," ","deploy stops with exit 64 and tells you which flag settles it, rather than"," ","exiting successfully having shipped nothing."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Deploy waits for the deployment to finish and exits non-zero if it fails. Add"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--no-wait"]}," to return as soon as the upload is accepted — useful when a later"," ","job polls, but it means a green step no longer means a running function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Other flags that matter in a pipeline:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Flag"},"children":["Flag"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Effect"},"children":["Effect"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--public"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Deploy to the public namespace (internet-accessible)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--private"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Deploy to the private namespace (internal access only)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--force"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Deploy despite validation warnings instead of prompting"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--managed-keys"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Use a managed access key for this function"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--no-docs"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Skip doc generation"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--strict-audit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Fail the deploy on dependency audit findings at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--audit-level"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--audit-level <level>"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Severity the audit reports on (default ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["high"]},")"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--public"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--private"]}," explicitly. Without either, deploy reuses the"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accessLevel"]}," that an earlier deploy saved in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]},", and a"," ","non-interactive deploy with nothing saved picks public. Relying on that means a"," ","change to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," can change the namespace without the pipeline saying so."," ","The examples below pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--public"]}," so the function can be called from the"," ","internet. Pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--private"]}," instead for internal access only."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"github-actions","__idx":4},"children":["GitHub Actions"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"name: Deploy function\n\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: '24'\n\n      - run: npm install -g @sinch/cli@0.8.1\n\n      - name: Deploy\n        env:\n          SINCH_PROJECT_ID: ${{ secrets.SINCH_PROJECT_ID }}\n          SINCH_KEY_ID: ${{ secrets.SINCH_KEY_ID }}\n          SINCH_KEY_SECRET: ${{ secrets.SINCH_KEY_SECRET }}\n          # Function secrets: one line per key left blank in .env.example\n          STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}\n        working-directory: my-function\n        run: |\n          cp .env.example .env\n          sinch functions deploy --non-interactive --public\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"gitlab-ci","__idx":5},"children":["GitLab CI"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"deploy:\n  image: node:24\n  only:\n    - main\n  before_script:\n    - npm install -g @sinch/cli@0.8.1\n  script:\n    - cd my-function\n    - cp .env.example .env\n    - sinch functions deploy --non-interactive --public\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_PROJECT_ID"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_ID"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_SECRET"]}," under"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Settings → CI/CD → Variables"]},", with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_KEY_SECRET"]}," marked ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Masked"]},". Add"," ","each function secret there too, masked, with the same name as its blank key in"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env.example"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"reading-output-in-a-script","__idx":6},"children":["Reading output in a script"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]}," prints the result to stdout with no spinner or table, so it can be"," ","piped straight into ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"sinch functions list --json | jq -r '.functions[] | select(.status != \"Running\") | .name'\nsinch functions status --json | jq -r '.deployment.status'\nsinch templates list --runtime node --json | jq -r '.templates[].name'\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["functions list"]}," returns the first 50 functions. Pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--limit"]}," (up to 200) and"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--page"]}," to read more."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["functions deploy --json"]}," prints one object and nothing else, so a pipeline can"," ","pick the deployed URL straight out of it:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"URL=$(sinch functions deploy --non-interactive --public --json | jq -r '.url')\ncurl --fail \"$URL\"\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"name\": \"my-function\",\n  \"functionId\": \"01M10PWQBM37KJSGJW0CZTP3CF\",\n  \"runtime\": \"node\",\n  \"status\": \"Running\",\n  \"accessLevel\": \"public\",\n  \"url\": \"https://supreme-hertz.fn.sinch.com\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["warnings"]}," carries anything pre-flight validation flagged, a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]}," tracked in"," ","git for instance, and warnings from the deploy itself. Validation warnings also"," ","go to stderr, so they are visible in the job log even though stdout stays a"," ","single document. The key is absent when there are none."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Deploy prints this document only after the function has shipped, so a check on"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["warnings"]}," here cannot stop a deploy. To fail closed on warnings for a Node.js"," ","function, run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["validate"]}," first. It deploys nothing and prints ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["valid"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["errors"]}," ","and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["warnings"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"set -o pipefail\nsinch functions validate --json | jq -e '.valid and (.warnings | length == 0)'\nsinch functions deploy --non-interactive --public --json\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pipefail"]}," in any step that pipes the CLI into ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq"]},". Without it, the step"," ","takes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["jq"]},"'s exit code and can pass after the CLI failed."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}," is the settled state. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]}," waits for the function to reach"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Running"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Failed"]}," before printing, so a URL in the payload is a URL you can"," ","call. The wait has two parts. Deploy first follows the deployment for up to 5"," ","minutes, and exits 104 if it does not finish. Then it checks the status for up"," ","to 3 more minutes. If neither state is reached by then, deploy prints the last"," ","status it read and still exits 0, so check that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}," is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Running"]},". Under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--no-wait"]}," there is no URL yet and the field is omitted"," ","rather than reported as null."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["On failure, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["deploy --json"]}," prints ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"error\": \"...\", \"exitCode\": 78}"]}," to stdout and exits"," ","with that code. Missing or rejected credentials (100 and 101) are the exception:"," ","the object is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"error\": \"...\"}"]}," with no ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["exitCode"]},", so read the code from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["$?"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"update-checks-in-ci","__idx":7},"children":["Update checks in CI"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The CLI never checks for or installs updates in CI. The check is skipped when any of these is true:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]}," is set, or the process is running in Docker"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["stdout is not a TTY (so anything piped or redirected)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--non-interactive"]}," is present"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you run somewhere that does not set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]},", set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SINCH_NO_AUTO_UPDATE"]}," so the"," ","CLI never installs an update on its own:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"yaml","header":{"controls":{"copy":{}}},"source":"env:\n  SINCH_NO_AUTO_UPDATE: '1'\n","lang":"yaml"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It stops the install only. Unless one of the conditions above holds, the check"," ","and the \"Update available\" notice still run."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pin an exact version in CI regardless, so a pipeline cannot change behaviour because a new release"," ","landed:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"npm install -g @sinch/cli@0.8.1\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/assets/changelog.7d7a5df7a851ab2a0939d4e5c01e29b5c1df3ca25f16d0f7497c39e61781feb6.8a245974.md"},"children":["changelog"]}," before you bump the pin."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"exit-codes","__idx":8},"children":["Exit codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The CLI follows BSD ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sysexits"]}," conventions plus a Sinch-specific range, so a"," ","pipeline can tell a bad credential apart from a bad request."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Code"},"children":["Code"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Usual cause in CI"},"children":["Usual cause in CI"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["0"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Success"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Generic failure"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Also an unknown command, an unknown option or a missing argument"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["64"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Usage error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A flag with an empty value, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["init --area-code"]}," without ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--rent-number"]},", a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["voice v1"]}," command with no application key, or deploy warnings with no terminal and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CI"]}," unset"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["65"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Data error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Malformed credentials file"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["66"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No input"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A named file does not exist, a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sip"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["conversation"]}," delete, remove or stop needs confirmation and there is no terminal, or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]}," needs a flag in place of a prompt"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["69"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unavailable"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["API refused the connection, or answered HTTP 429 or 5xx"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["70"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Internal error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["73"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cannot create"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not returned by any command today"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["74"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["I/O error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No response from the API, such as a DNS failure, a reset connection or a request past the 60-second timeout"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["77"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No permission"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Key lacks access to the project"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["78"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Config error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Missing or contradictory configuration"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["100"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Auth required"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The three environment variables are not set"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["101"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Auth failed"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Wrong or revoked key secret"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["102"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Not found"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No such function or template"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["103"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Conflict"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Name already taken"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["104"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Timeout"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Deployment did not become ready in time, or the network connection to the API timed out"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["130"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Interrupted"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions init"]}," stopped with Ctrl+C"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["100"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["101"]}," are the two worth branching on: both mean the pipeline's"," ","credentials are wrong, and no retry will help. Missing credentials do not always"," ","reach 100: with no credentials set, a credential key left blank in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]},", such"," ","as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PROJECT_ID_API_SECRET"]},", fails validation first, so deploy exits 78."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Capture the code with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|| code=$?"]},". By default GitHub Actions runs ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["run"]}," steps"," ","with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bash -e"]},", and GitLab stops a job at its first failing line, so a plain"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["case $?"]}," after a failed deploy is never reached:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"code=0\nsinch functions deploy --non-interactive || code=$?\ncase $code in\n  0)   echo \"deployed\" ;;\n  100|101) echo \"check SINCH_KEY_ID / SINCH_KEY_SECRET\"; exit 1 ;;\n  104) echo \"deploy timed out; check sinch functions status\"; exit 1 ;;\n  *)   echo \"deploy failed\"; exit 1 ;;\nesac\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"secrets-a-function-needs-at-runtime","__idx":9},"children":["Secrets a function needs at runtime"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For a Node.js function, list each secret in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env.example"]}," (and your local"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]},") with an empty value, for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["STRIPE_SECRET_KEY="]},". Then add a masked CI variable with the same name and pass"," ","it to the deploy step. At deploy, the CLI fills each empty key from the job's"," ","environment and sends it to the platform as a secret. Keys that have a value"," ","ship as plain configuration, except a name that looks like a credential: its"," ","last word is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KEY"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["KEYS"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["APIKEY"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SECRET"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SECRETS"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOKEN"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PASSWORD"]},","," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PASSWD"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PASS"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PWD"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PASSPHRASE"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CREDENTIAL"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CREDENTIALS"]},", and no word"," ","in it is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PUBLIC"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PUBLISHABLE"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OPENAI_API_KEY"]},", but not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MAX_TOKENS"]}," or"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PUBLIC_KEY"]},"). Those ship as secrets. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PROJECT_ID_API_KEY"]}," and"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VOICE_APPLICATION_KEY"]}," are IDs, not secrets, so they stay plain. A key stays a"," ","secret once it has shipped as one, so rename a key to keep its value plain. Keep"," ","secret values out of ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env.example"]}," either way."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch.json"]}," does not carry secrets to the deployed function. Deploy does not"," ","read its ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["variables"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch functions dev"]}," reads them, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch env init"]}," ","copies them into a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[".env"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Do not add a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sinch secrets add"]}," step to CI. It writes to the OS keychain of the"," ","machine that runs it, which on Linux needs a Secret Service daemon"," ","(",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["secret-tool"]},"). Use a CI variable instead."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"debugging-a-failing-job","__idx":10},"children":["Debugging a failing job"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"sh","header":{"controls":{"copy":{}}},"source":"SINCH_DEBUG=2 sinch functions deploy --non-interactive\n","lang":"sh"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Level 2 logs HTTP method, URL and status. Level 3 adds the request headers,"," ","redacted. Response bodies are not logged, except the body of a failed OAuth"," ","exchange, which is logged in full. See"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/functions/cli/configuration#debug-environment-variables"},"children":["Configuration → Debug environment variables"]}," ","for every level."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"log-output","__idx":11},"children":["Log output"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Progress reporting goes to stderr and animates only when stderr is a terminal."," ","With no terminal, as on any CI runner or with a local ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2> file"]},", each step"," ","prints as plain lines instead, so the log does not fill with spinner"," ","frames and cursor escapes. Nothing to configure."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["--json"]}," there is no progress output at all: stdout carries the one"," ","document. Validation warnings still go to stderr, as described in"," ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#reading-output-in-a-script"},"children":["Reading output in a script"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"see-also","__idx":12},"children":["See also"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/functions/cli/configuration"},"children":["Configuration"]}," — profiles, config file, global flags"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/functions/cli/commands/functions"},"children":["functions command reference"]}]}]}]},"headings":[{"value":"CI/CD","id":"cicd","depth":1},{"value":"Authentication","id":"authentication","depth":2},{"value":"Before the first deploy","id":"before-the-first-deploy","depth":2},{"value":"Deploying a function","id":"deploying-a-function","depth":2},{"value":"GitHub Actions","id":"github-actions","depth":2},{"value":"GitLab CI","id":"gitlab-ci","depth":2},{"value":"Reading output in a script","id":"reading-output-in-a-script","depth":2},{"value":"Update checks in CI","id":"update-checks-in-ci","depth":2},{"value":"Exit codes","id":"exit-codes","depth":2},{"value":"Secrets a function needs at runtime","id":"secrets-a-function-needs-at-runtime","depth":2},{"value":"Debugging a failing job","id":"debugging-a-failing-job","depth":2},{"value":"Log output","id":"log-output","depth":2},{"value":"See also","id":"see-also","depth":2}],"frontmatter":{"seo":{"title":"CI/CD | Sinch CLI | Sinch","description":"Run the Sinch CLI in pipelines: environment variable authentication, --non-interactive, --json and exit codes."}},"lastModified":"2026-10-12T21:36:30.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/functions/cli/ci-cd","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}