├── .deepsource.toml ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── config.yml └── workflows │ ├── env-manifest.yml │ ├── nodejs.yml │ └── template-docs.yml ├── .gitignore ├── .husky ├── .gitignore ├── pre-commit └── pre-push ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── __mocks__ └── got.js ├── _helpers ├── add-template.js ├── generate-env-variable-manifest.js ├── import-template.js ├── meta-template │ ├── .env.example │ ├── .owners │ ├── CHANGELOG.md │ ├── README.md │ ├── _package.json │ ├── assets │ │ └── index.html │ ├── functions │ │ ├── blank.js │ │ └── hello-messaging.protected.js │ └── tests │ │ ├── blank.test.js │ │ └── hello-messaging.test.js ├── template-docs.js ├── test-suite │ ├── cypress.js │ ├── e2e-config.js │ ├── index.js │ └── serverless.js └── utils │ └── new-template.js ├── ai-assistants-samples ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── index.html │ └── utils.private.js ├── functions │ ├── channels │ │ ├── conversations │ │ │ ├── flex-webchat.protected.js │ │ │ ├── messageAdded.protected.js │ │ │ └── response.js │ │ └── messaging │ │ │ ├── incoming.protected.js │ │ │ └── response.js │ └── tools │ │ ├── flex-handover.js │ │ ├── google-maps.js │ │ ├── internet-search.js │ │ ├── studio-handover.js │ │ └── ui-tools.js ├── package.json └── tests │ ├── channels │ ├── conversations │ │ ├── flex-webchat.test.js │ │ ├── messageAdded.test.js │ │ └── response.test.js │ └── messaging │ │ ├── incoming.test.js │ │ └── response.test.js │ └── tools │ ├── flex-handover.test.js │ ├── google-maps.test.js │ ├── studio-handover.test.js │ └── ui-tools.test.js ├── airtable ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ ├── broadcast-sms.js │ └── save-sms.protected.js ├── package.json └── tests │ ├── broadcast-sms.test.js │ └── save-sms.test.js ├── blank ├── .env.example ├── README.md ├── changelog.md ├── functions │ └── blank.js ├── package.json └── tests │ └── blank.test.js ├── block-spam-calls ├── .env ├── CHANGELOG.md ├── README.md ├── assets │ └── index.html ├── functions │ └── block-spam-calls.protected.js ├── package.json └── tests │ ├── block-spam-calls.test.js │ └── spam-filter-results │ ├── clean-marchex.json │ ├── clean-nomorobo.json │ ├── failed-nomorobo.json │ ├── spam-marchex.json │ └── spam-nomorobo.json ├── blocklist-call ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── blocklist-call.protected.js ├── package.json └── tests │ └── blocklist-call.test.js ├── caller-id-forwarding ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── forward-call.protected.js ├── package.json └── tests │ └── forward-call.test.js ├── chat-token ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── chat-token.js ├── package.json └── tests │ └── chat-token.test.js ├── conference-caller-gated ├── .env ├── .owners ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── gated-conference.protected.js ├── package.json └── tests │ └── gated-conference.test.js ├── conference-pin ├── .env ├── .owners ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── conference-with-pin.protected.js ├── package.json └── tests │ └── conference-with-pin.test.js ├── conference-verify ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ ├── join-conference.protected.js │ └── verify-conference.protected.js ├── package.json └── tests │ ├── join-conference.test.js │ └── verify-conference.test.js ├── conference ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── conference.protected.js ├── package.json └── tests │ └── conference.test.js ├── contact-form ├── .env ├── README.md ├── assets │ ├── index.html │ └── style.css ├── changelog.md ├── contact-form.gif ├── functions │ └── send-email.js ├── package.json └── tests │ └── send-email.test.js ├── conversations ├── .env ├── README.md ├── changelog.md ├── functions │ └── generate-scoped-webhook.js ├── package.json └── tests │ └── generate-scoped-webhook.test.js ├── covid-vaccine-faq-bot ├── .env ├── .owners ├── README.md ├── assets │ ├── client.js │ ├── index.html │ ├── make-your-own │ │ └── studioflow-agent-generator.ipynb │ ├── prebuilt-chatbots │ │ ├── india_mohfw_agent.zip │ │ └── usa_ama_agent.zip │ ├── service-account-key.private.json │ ├── studio_flow.private.js │ └── styles.css ├── changelog.md ├── functions │ ├── auth.private.js │ ├── check-existing-flow.js │ ├── check-googlejson-config.js │ ├── es-dialogflow-detect-intent.js │ ├── return-config.js │ └── setup.js ├── package.json ├── prerequisites │ ├── dialogflow-config.md │ └── google-cloud-platform-config.md └── tests │ ├── check-googlejson-config.test.js │ ├── covid-vaccine-faq-bot.test.js │ └── service-account-key.test.json ├── docs ├── BUILD_TEMPLATE.md ├── CONTRIBUTING.md ├── TESTING.md ├── USING_FUNCTIONS.md ├── images │ ├── APP_INFO_V2_comment.png │ ├── EDIT_CODE_comment.png │ ├── add-dependencies-v2.png │ ├── add-function.png │ ├── create-env-variables-v2.png │ └── download_asset.png ├── static │ └── v1 │ │ ├── Inter.var.woff2 │ │ ├── ce-helpers.js │ │ ├── ce-paste-theme.css │ │ ├── favicon.ico │ │ ├── success.svg │ │ └── video_logo.png └── templates.md ├── env-variables.manifest.json ├── experimental-flex-dialpad ├── .env.example ├── CHANGELOG.md ├── README.md ├── assets │ ├── flex-dialpad-addon.js.LICENSE.private.txt │ ├── flex-dialpad-addon.js.private.map │ ├── flex-dialpad-addon.private.js │ ├── helpers │ │ ├── flex-plugins.private.js │ │ └── setup.private.js │ └── index.html ├── functions │ ├── dialpad-utils.js │ ├── external-transfer │ │ ├── add-conference-participant.js │ │ ├── get-call-properties.js │ │ ├── hold-conference-participant.js │ │ ├── remove-conference-participant.js │ │ └── update-conference-participant.js │ ├── internal-call │ │ ├── agent-join-conference.protected.js │ │ ├── agent-outbound-join.protected.js │ │ ├── call-outbound-join.protected.js │ │ ├── cleanup-rejected-task.js │ │ └── hold-call.js │ └── setup.js ├── package.json ├── tests │ └── setup.test.js └── ui-src │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── public │ └── appConfig.example.js │ ├── screenshots │ └── outbound-filter.png │ ├── src │ ├── DialpadPlugin.js │ ├── components │ │ ├── ExternalTransfer │ │ │ ├── ConferenceButton.js │ │ │ ├── ConferenceDialog.js │ │ │ ├── ConferenceMonitor.js │ │ │ ├── ParticipantActionsButtons.js │ │ │ ├── ParticipantName.js │ │ │ ├── ParticipantStatus.js │ │ │ ├── ParticipantStatusContainer.js │ │ │ └── index.js │ │ └── InternalCall │ │ │ ├── InternalDialpad.jsx │ │ │ └── index.js │ ├── customActions │ │ ├── externalTransfer │ │ │ └── index.js │ │ ├── index.js │ │ └── internalCall │ │ │ └── index.js │ ├── helpers │ │ ├── ConferenceService.js │ │ └── request.js │ ├── index.js │ ├── notifications │ │ └── index.js │ ├── setupTests.js │ └── styling │ │ └── theme.js │ ├── webpack.config.js │ └── webpack.dev.js ├── forward-call ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── forward-call.protected.js ├── package.json └── tests │ └── forward-call.test.js ├── forward-message-mailgun ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── forward-message-mailgun.protected.js ├── package.json └── tests │ └── forward-message-mailgun.test.js ├── forward-message ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── forward-message.protected.js ├── package.json └── tests │ └── forward-message.test.js ├── frontline-quickstart ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── conversations-webhooks-info.png │ ├── index.html │ ├── providers │ │ └── customers.private.js │ └── style.css ├── functions │ └── callbacks │ │ ├── crm.protected.js │ │ ├── outgoing-conversation.protected.js │ │ ├── routing.protected.js │ │ ├── templates.protected.js │ │ └── twilio-conversations.protected.js ├── package.json └── tests │ ├── assets │ └── providers │ │ └── customers.test.js │ └── callbacks │ ├── crm.test.js │ ├── outgoing-conversation.test.js │ ├── routing.test.js │ ├── templates.test.js │ └── twilio-conversations.test.js ├── funlet-call-me ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-call-me.protected.js ├── package.json └── tests │ ├── funlet-call-me.test.js │ ├── test-call-me.sh │ ├── test-whisper.sh │ ├── twimlet-call-me.log │ └── twimlet-whisper.log ├── funlet-echo ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-echo.protected.js ├── package.json └── tests │ ├── funlet-echo.test.js │ ├── test-echo.sh │ └── twimlet-echo.log ├── funlet-find-me ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-find-me.protected.js ├── package.json └── tests │ ├── funlet-find-me.test.js │ ├── test-find-me.sh │ ├── test-whisper.sh │ ├── twimlet-find-me.log │ └── twimlet-whisper.log ├── funlet-forward ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-forward.protected.js ├── package.json └── tests │ ├── funlet-forward.test.js │ ├── test-forward.sh │ └── twimlet-forward.log ├── funlet-simple-menu ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-simple-menu.protected.js ├── package.json └── tests │ ├── funlet-simple-menu.test.js │ ├── test-simple-menu.sh │ └── twimlet-simple-menu.log ├── funlet-simple-message ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-simple-message.protected.js ├── package.json └── tests │ ├── funlet-simple-message.test.js │ ├── test-simple-message.sh │ └── twimlet-simple-message.log ├── funlet-simulring ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-simulring.protected.js ├── package.json └── tests │ ├── funlet-simulring.test.js │ ├── test-simulring.sh │ ├── test-whisper.sh │ ├── twimlet-simulring.log │ └── twimlet-whisper.log ├── funlet-whisper ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── funlet-whisper.protected.js ├── package.json └── tests │ ├── funlet-whisper.test.js │ ├── test-whisper.sh │ └── twimlet-whisper.log ├── google-sheets ├── .env ├── README.md ├── assets │ ├── auth.private.json │ └── index.html ├── changelog.md ├── functions │ ├── check-sheets-config.js │ └── log-sms.protected.js ├── package.json └── tests │ ├── auth.test.json │ ├── check-sheets-config.test.js │ └── log-sms.test.js ├── hello-messaging ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── hello-messaging.protected.js ├── package.json └── tests │ └── hello-messaging.test.js ├── hello-voice ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── hello-voice.protected.js ├── package.json └── tests │ └── hello-voice.test.js ├── hello-world ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── cypress │ ├── integration │ │ └── hello-world.spec.js │ ├── plugins │ │ └── index.js │ └── support │ │ ├── commands.js │ │ └── index.js ├── e2e.js ├── functions │ └── hello-world.js ├── package.json └── tests │ └── hello-world.test.js ├── http-redirect ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── redirect.js ├── package.json └── tests │ └── redirect.test.js ├── hunt ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── hunt.js ├── package.json └── tests │ └── hunt.test.js ├── international-telephone-input ├── .env ├── README.md ├── assets │ ├── index.html │ └── styles.css ├── changelog.md ├── functions │ └── lookup.js ├── package.json └── tests │ └── lookup.test.js ├── json-webhook ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── event-to-webhook.protected.js ├── package.json └── tests │ └── event-to-webhook.test.js ├── list-numbers ├── .env ├── .owners ├── Changelog.md ├── assets │ ├── index.html │ └── styles.css ├── e2e.js ├── functions │ ├── get_account.js │ ├── list_accounts.js │ ├── list_numbers.js │ └── mask_account.js ├── package.json ├── readme.txt └── tests │ └── .gitkeep ├── list_accounts.js ├── lookup ├── .env ├── README.md ├── assets │ ├── index.html │ └── styles.css ├── changelog.md ├── functions │ └── lookup.js ├── package.json └── tests │ └── lookup.test.js ├── magic-links ├── .env ├── README.md ├── assets │ ├── email-template.html │ ├── index.html │ └── verify.html ├── changelog.md ├── functions │ ├── check-verify.js │ └── start-verify.js ├── magic-link-demo.gif ├── package.json └── tests │ ├── check-verify.test.js │ └── start-verify.test.js ├── masked-number ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── relay-sms.protected.js ├── package.json └── tests │ └── relay-sms.test.js ├── mcp-server ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ └── index.html ├── functions │ ├── mcp.protected.js │ ├── req.private.js │ ├── res.private.js │ └── server.private.js ├── package.json └── tests │ └── .gitkeep ├── never-gonna-give-you-up ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── never-gonna-give-you-up.protected.js ├── never-gonna-give-you-up.js ├── never-gonna-give-you-up.test.js ├── package.json └── tests │ └── never-gonna-give-you-up.test.js ├── package-lock.json ├── package.json ├── passkeys-backend ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── .well-know │ │ ├── apple-app-site-association │ │ └── assetlinks.json │ ├── index.html │ └── services │ │ └── helpers.private.js ├── functions │ ├── authentication │ │ ├── start.js │ │ └── verification.js │ └── registration │ │ ├── start.js │ │ └── verification.js ├── package.json └── tests │ ├── authentication-start.test.js │ ├── authentication-verification.test.js │ ├── registration-start.test.js │ └── registration-verification.test.js ├── patient-appointment-management ├── .env ├── README.md ├── assets │ ├── architecture.png │ ├── aws │ │ ├── cloudformation-stack-application.private.yml │ │ ├── cloudformation-stack-bucket.private.yml │ │ ├── cloudformation-stack-deployer.private.yml │ │ ├── query_appointment_history.private.js │ │ ├── query_appointment_state.private.js │ │ └── send_appointment_reminders.private.js │ ├── controller.js │ ├── index.html │ ├── state-transition.png │ ├── studio-flow-template.private.json │ ├── styles.css │ └── token-flow.png ├── changelog.md ├── functions │ ├── auth.private.js │ ├── deployment │ │ ├── check-aws-application.js │ │ ├── check-aws-bucket.js │ │ ├── check-query.js │ │ ├── check-studio-flow.js │ │ ├── check.js │ │ ├── deploy-aws-application.js │ │ ├── deploy-aws-bucket.js │ │ ├── deploy-aws-code.js │ │ ├── deploy-studio-flow.js │ │ ├── execute-query.js │ │ ├── simulate-parameters.js │ │ ├── simulation-event.js │ │ └── test-deployment.private.js │ ├── get-datetime-parts.protected.js │ ├── helpers.private.js │ ├── login.js │ ├── mfa.js │ ├── refresh-token.js │ ├── save-booked.protected.js │ ├── save-cancel.protected.js │ ├── save-canceled.protected.js │ ├── save-confirm.protected.js │ ├── save-confirmed.protected.js │ ├── save-modified.protected.js │ ├── save-noshowed.protected.js │ ├── save-opted-out.protected.js │ ├── save-remind.protected.js │ └── save-rescheduled.protected.js ├── package.json └── tests │ ├── get-datetime-parts.test.js │ ├── helpers.test.js │ ├── save-booked.test.js │ ├── save-cancel.test.js │ ├── save-canceled.test.js │ ├── save-confirm.test.js │ ├── save-confirmed.test.js │ ├── save-modified.test.js │ ├── save-noshow.test.js │ ├── save-remind.test.js │ └── save-rescheduled.test.js ├── reminder-message ├── .env.example ├── CHANGELOG.md ├── README.md ├── assets │ └── index.html ├── functions │ └── respond.protected.js ├── package.json └── tests │ └── respond.test.js ├── segment-event-notification ├── .env ├── README.md ├── assets │ ├── index.html │ ├── segment-event-tester.png │ └── segment-webhook-settings.png ├── changelog.md ├── functions │ └── track-sms.js ├── package.json └── tests │ └── track-sms.test.js ├── sip-quickstart ├── .env ├── README.md ├── assets │ ├── admin │ │ ├── actions.private.js │ │ ├── admin-client.js │ │ ├── admin.css │ │ ├── index.html │ │ ├── shared.private.js │ │ └── statuses.private.js │ ├── extensions.private.js │ ├── index.html │ └── site.css ├── changelog.md ├── functions │ ├── admin │ │ ├── check-status.js │ │ ├── login.js │ │ └── perform-action.js │ ├── extension-menu.js │ ├── outbound-calls.js │ └── sip-configuration.js ├── package.json └── tests │ ├── admin │ ├── check-status.test.js │ ├── login.test.js │ ├── perform-action.test.js │ └── private │ │ ├── actions.test.js │ │ ├── shared.test.js │ │ └── statuses.test.js │ ├── extension-menu.test.js │ └── outbound-calls.test.js ├── sms-broadcast ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── broadcast.protected.js ├── package.json └── tests │ └── broadcast.test.js ├── sms-notifications ├── .env ├── README.md ├── assets │ ├── index.html │ └── index.js ├── changelog.md ├── functions │ └── send-messages.js ├── package.json └── tests │ └── send-messages.test.js ├── stripe-payment-link-sms ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ ├── create-invoice.protected.js │ └── send-invoice-sms.js ├── images │ ├── stripe-webhook-dashboard.png │ └── twilio-console-messaging-webhook.png ├── package.json └── tests │ ├── create-invoice.test.js │ └── send-invoice-sms.test.js ├── stripe-sms-receipt ├── .env ├── README.md ├── assets │ ├── index.html │ └── stripe_fixtures │ │ └── create_customer_and_payment.private.json ├── changelog.md ├── functions │ └── send-sms-receipt.js ├── package.json └── tests │ └── send-sms-receipt.test.js ├── sync-token ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── sync-token.js ├── package.json └── tests │ └── sync-token.test.js ├── temp-storage ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── temp-storage.js ├── package.json └── tests │ └── temp-storage.test.js ├── templates.json ├── test ├── all-templates.test.js └── test-helper.js ├── transfers ├── .env ├── .owners ├── assets │ ├── index.html │ └── styles.css ├── functions │ ├── fetch_sid.js │ ├── get_account.js │ ├── list_accounts.js │ ├── mask_account.js │ └── transfer.js ├── package.json ├── readme.txt └── tests │ └── .gitkeep ├── vaccine-standby ├── .env ├── README.md ├── assets │ ├── client.js │ ├── index.html │ ├── standby-list-diagram.png │ ├── studio_flow.private.js │ └── styles.css ├── changelog.md ├── functions │ ├── auth.private.js │ ├── check-existing-flow.js │ ├── get-studio-executions.js │ ├── login.js │ ├── return-config.js │ └── setup.js └── package.json ├── verified-broadcast ├── .env ├── README.md ├── assets │ ├── auth.private.js │ ├── broadcast.html │ ├── index.html │ ├── setup.private.js │ └── subscribe.html ├── changelog.md ├── functions │ ├── broadcast.js │ ├── start-verify.js │ └── subscribe.js ├── package.json └── tests │ ├── auth.test.js │ ├── broadcast.test.js │ ├── setup.test.js │ ├── start-verify.test.js │ └── subscribe.test.js ├── verify-dashboard ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ ├── check-verify.js │ ├── service-details.js │ ├── start-verify.js │ ├── status-verify.js │ └── update-verify.js ├── package.json └── tests │ ├── check-verify.test.js │ ├── service-details.test.js │ ├── start-verify.test.js │ ├── status-verify.test.js │ └── update-verify.test.js ├── verify-prefill ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── index.html │ └── script.js ├── functions │ ├── fetch-user-data.js │ ├── send-otp.js │ └── verify-otp.js ├── image.png ├── package.json └── tests │ ├── fetch-user-data.test.js │ ├── send-otp.test.js │ └── verify-otp.test.js ├── verify-push-authy-backend ├── .env ├── README.md ├── assets │ ├── digest-message.private.js │ ├── index.html │ └── missing-params.private.js ├── changelog.md ├── functions │ ├── challenge-status.js │ ├── create-challenge.js │ ├── generate-qr-code.js │ └── list-factors.js ├── package.json └── tests │ ├── challenge-status.test.js │ ├── create-challenge.test.js │ ├── digest-message.test.js │ ├── generate-qr-code.test.js │ ├── list-factors.test.js │ └── missing-params.test.js ├── verify-push-backend ├── .env ├── README.md ├── assets │ ├── digest-message.private.js │ ├── index.html │ └── missing-params.private.js ├── changelog.md ├── functions │ ├── access-token.js │ ├── challenge-status.js │ ├── create-challenge.js │ └── list-factors.js ├── package.json └── tests │ ├── access-token.test.js │ ├── challenge-status.test.js │ ├── create-challenge.test.js │ ├── digest-message.test.js │ ├── list-factors.test.js │ └── missing-params.test.js ├── verify-retry ├── .env ├── README.md ├── assets │ ├── index.html │ └── utils.private.js ├── changelog.md ├── functions │ ├── check-verify.js │ └── start-verify.js ├── package.json └── tests │ ├── check-verify.test.js │ └── start-verify.test.js ├── verify-sna ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── data │ │ ├── index.private.js │ │ └── operations.private.js │ ├── index.html │ ├── services │ │ ├── constants.private.js │ │ ├── helpers.private.js │ │ └── verifications.private.js │ ├── style.css │ └── utils.js ├── functions │ ├── get-verifications.js │ ├── verify-check.js │ └── verify-start.js ├── package.json └── tests │ ├── connect-to-sync-map.test.js │ ├── get-verifications.test.js │ ├── verify-check.test.js │ └── verify-start.test.js ├── verify-totp-sms ├── .env ├── .owners ├── README.md ├── assets │ ├── helpers.js │ ├── index.html │ ├── login.html │ ├── styles.css │ ├── totp-setup.html │ └── utils.private.js ├── changelog.md ├── cypress.config.js ├── cypress │ ├── fixtures │ │ └── example.json │ ├── integration │ │ └── tests.js │ ├── plugins │ │ └── index.js │ └── support │ │ ├── commands.js │ │ ├── e2e.js │ │ └── index.js ├── e2e.js ├── functions │ ├── check-otp.js │ ├── create-challenge.js │ ├── create-factor.js │ ├── send-otp.js │ └── verify-new-factor.js ├── package.json └── tests │ ├── check-otp.test.js │ ├── create-challenge.test.js │ ├── create-factor.test.js │ ├── send-otp.test.js │ └── verify-new-factor.test.js ├── verify-totp ├── .env ├── README.md ├── assets │ ├── helpers.js │ ├── index.html │ ├── styles.css │ └── utils.private.js ├── changelog.md ├── functions │ ├── create-challenge.js │ ├── create-factor.js │ └── verify-new-factor.js ├── package.json └── tests │ ├── create-challenge.test.js │ ├── create-factor.test.js │ └── verify-new-factor.test.js ├── verify ├── .env ├── README.md ├── assets │ ├── index.html │ ├── locales.js │ └── styles.css ├── changelog.md ├── functions │ ├── check-verify.js │ └── start-verify.js ├── package.json ├── phone-verification-v3.gif └── tests │ ├── check-verify.test.js │ └── start-verify.test.js ├── video-token-backend ├── .env.example ├── .owners ├── CHANGELOG.md ├── README.md ├── assets │ ├── app.js │ ├── index.html │ └── styles.css ├── functions │ ├── initialize.js │ └── token.js ├── package.json └── tests │ ├── app.test.js │ ├── initialize.test.js │ └── token.test.js ├── video-token ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ └── video-token.js ├── package.json └── tests │ └── video-token.test.js ├── video ├── .env ├── README.md ├── assets │ ├── camera_permissions.png │ ├── conference.css │ ├── conference.html │ ├── conference.js │ └── index.html ├── changelog.md ├── functions │ └── video-token.js ├── package.json └── tests │ └── video-token.test.js ├── voice-client-javascript ├── .env ├── README.md ├── assets │ ├── admin │ │ ├── actions.private.js │ │ ├── admin-client.js │ │ ├── admin.css │ │ ├── index.html │ │ ├── shared.private.js │ │ └── statuses.private.js │ ├── index.html │ ├── quickstart.js │ └── site.css ├── changelog.md ├── functions │ ├── admin │ │ ├── check-status.js │ │ ├── login.js │ │ └── perform-action.js │ ├── client-voice-twiml-app.js │ └── voice-token.js ├── package-lock.json ├── package.json └── tests │ ├── admin │ ├── check-status.test.js │ ├── login.test.js │ ├── perform-action.test.js │ └── private │ │ ├── actions.test.js │ │ ├── shared.test.js │ │ └── statuses.test.js │ ├── client-voice-twiml-app.test.js │ └── voice-token.test.js ├── voice-ivr ├── .env ├── README.md ├── assets │ └── index.html ├── changelog.md ├── functions │ ├── handle-user-input.protected.js │ └── voice-ivr.protected.js ├── package.json └── tests │ ├── handle-user-input.test.js │ └── voice-ivr.test.js ├── voice-javascript-sdk ├── .env ├── README.md ├── assets │ ├── admin │ │ ├── actions.private.js │ │ ├── admin-client.js │ │ ├── admin.css │ │ ├── index.html │ │ ├── shared.private.js │ │ └── statuses.private.js │ ├── index.html │ ├── quickstart.js │ └── site.css ├── changelog.md ├── functions │ ├── admin │ │ ├── check-status.js │ │ ├── login.js │ │ └── perform-action.js │ ├── voice-javascript-sdk-twiml-app.js │ └── voice-token.js ├── package.json └── tests │ ├── admin │ ├── check-status.test.js │ ├── login.test.js │ ├── perform-action.test.js │ └── private │ │ ├── actions.test.js │ │ ├── shared.test.js │ │ └── statuses.test.js │ ├── voice-javascript-sdk-twiml-app.test.js │ └── voice-token.test.js └── voicemail ├── .env ├── README.md ├── assets └── index.html ├── changelog.md ├── functions ├── recording.js └── voicemail.protected.js ├── package.json └── tests ├── recoding.test.js └── voicemail.test.js /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "javascript" 5 | enabled = true 6 | 7 | [[analyzers]] 8 | name = "shell" 9 | enabled = true 10 | 11 | [[analyzers]] 12 | name = "secrets" 13 | enabled = true 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /docs 3 | **/cypress/ 4 | experimental-flex-dialpad/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | node: true, 7 | }, 8 | extends: 'twilio', 9 | globals: { 10 | Atomics: 'readonly', 11 | SharedArrayBuffer: 'readonly', 12 | }, 13 | parserOptions: { 14 | ecmaVersion: 2018, 15 | }, 16 | rules: { 17 | 'prettier/prettier': 0, 18 | 'import/no-extraneous-dependencies': 0, 19 | 'no-console': 0, 20 | 'sonarjs/no-duplicate-string': 0, 21 | 'sonarjs/no-identical-functions': 0, 22 | 'func-names': 0, 23 | 'global-require': 0, 24 | 'no-shadow': 0, 25 | 'import/order': 0, 26 | 'max-classes-per-file': 0, 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # By default, assign reviews to the core reviewers team 2 | * @twilio-labs/function-core-reviewers 3 | 4 | # Assign all subdirectories to the apps review team 5 | # (more strict patterns later in this file will refine review assignment) 6 | 7 | /*/ @twilio-labs/function-reviewers 8 | 9 | # Assign core subdirectories to the core reviewers team 10 | /docs/ @twilio-labs/function-core-reviewers 11 | /.github/ @twilio-labs/function-core-reviewers 12 | /.husky/ @twilio-labs/function-core-reviewers 13 | /test/ @twilio-labs/function-core-reviewers 14 | /_helpers/ @twilio-labs/function-core-reviewers 15 | /__mocks__/ @twilio-labs/function-core-reviewers 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## Description 5 | 6 | 7 | 8 | ## Checklist 9 | 10 | 11 | 12 | - [ ] I ran `npm test` locally and it passed without errors. 13 | - [ ] I acknowledge that all my contributions will be made under the project's [license](../blob/main/LICENSE). 14 | 15 | 16 | 17 | ## Related issues 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | newIssueWelcomeComment: | 2 | Thank you so much for opening your first issue in this project! We'll try to get back to it as quickly as possible. While you are waiting...here's a random picture of a corgi (powered by [dog.ceo](https://dog.ceo)) 3 | 4 | ![picture of dog](https://images.dog.ceo/breeds/corgi-cardigan/n02113186_1447.jpg) 5 | 6 | firstPRMergeComment: > 7 | Congratulations on your first contribution to the Twilio Function Templates! 8 | 9 | If you are on the look out for more ways to contribute to open-source, 10 | check out a list of some of our repositories at https://github.com/twilio/opensource. 11 | 12 | To stay up-to-date with Twilio open source related updates, sign up here: https://twil.io/oss-updates 13 | 14 | And if you love Twilio as much as we do, make sure to check out our 15 | [Twilio Champions program](https://www.twilio.com/champions)! 16 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test, Lint, Format (Node.js) 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest] 11 | node-version: [current, lts/*] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - name: install latest npm 20 | run: npm install -g npm@latest 21 | - name: npm install 22 | run: npm install 23 | env: 24 | CI: true 25 | - name: npm run build --if-present 26 | run: npm run build --if-present 27 | env: 28 | CI: true 29 | - name: npm test 30 | run: npm test 31 | env: 32 | CI: true 33 | - name: npm run format:check 34 | run: npm run format:check 35 | env: 36 | CI: true 37 | NODE_ENV: test 38 | - name: npm run lint 39 | run: npm run lint 40 | env: 41 | CI: true 42 | -------------------------------------------------------------------------------- /.github/workflows/template-docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate template doc 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Use Node.js 16 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 16 18 | - name: install npm v8 19 | run: | 20 | npm install -g npm@8 21 | - name: generate doc 22 | run: | 23 | node ./_helpers/template-docs.js 24 | - name: check for changes 25 | id: changes 26 | run: | 27 | git diff --name-only | xargs 28 | echo "::set-output name=files::$(git diff --name-only | xargs)" 29 | - name: commit changes 30 | if: ${{ contains(steps.changes.outputs.files, 'docs/templates.md') }} 31 | run: | 32 | git config user.name twilio-labs-ci 33 | git config user.email 65686990+twilio-labs-ci@users.noreply.github.com 34 | git add docs/templates.md 35 | git commit -m "chore(docs): update templates doc" 36 | git push 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .twilio-functions 4 | package-lock.json 5 | .DS_Store 6 | **/.env 7 | .turbo 8 | cypress/screenshots/ 9 | cypress/videos/ 10 | **/cypress/screenshots/ 11 | **/cypress/videos/ 12 | **/.twiliodeployinfo -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo -e "Running prettier...\n" 5 | npm run format:changed && format_status=$? || format_status=$? 6 | 7 | if [ "$format_status" -eq "0" ]; then 8 | echo -e "\nprettier finished.\n" 9 | else 10 | cat < { 5 | if (this.shouldSucceed) { 6 | process.nextTick(resolve()); 7 | } else { 8 | process.nextTick(reject(new Error())); 9 | } 10 | }); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /_helpers/meta-template/.env.example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/_helpers/meta-template/.env.example -------------------------------------------------------------------------------- /_helpers/meta-template/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | # Insert your Github username here 5 | -------------------------------------------------------------------------------- /_helpers/meta-template/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /_helpers/meta-template/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{name}}", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {{dependencies}} 6 | } 7 | -------------------------------------------------------------------------------- /_helpers/meta-template/functions/blank.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | callback(null, {}); 3 | }; 4 | -------------------------------------------------------------------------------- /_helpers/meta-template/functions/hello-messaging.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const twiml = new Twilio.twiml.MessagingResponse(); 3 | twiml.message('Hello World'); 4 | callback(null, twiml); 5 | }; 6 | -------------------------------------------------------------------------------- /_helpers/meta-template/tests/blank.test.js: -------------------------------------------------------------------------------- 1 | const { handler } = require('../functions/blank'); 2 | 3 | describe('{{name}} function template', () => { 4 | it('Calls callback with empty JSON', () => { 5 | const callback = jest.fn(); 6 | handler({}, {}, callback); 7 | expect(callback).toHaveBeenCalledWith(null, {}); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /_helpers/meta-template/tests/hello-messaging.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../../test/test-helper'); 2 | const helloVoice = require('../functions/hello-messaging.protected').handler; 3 | const Twilio = require('twilio'); 4 | 5 | const context = {}; 6 | const event = {}; 7 | 8 | beforeAll(() => { 9 | helpers.setup(context); 10 | }); 11 | 12 | afterAll(() => { 13 | helpers.teardown(); 14 | }); 15 | 16 | test('returns a VoiceResponse', (done) => { 17 | const callback = (_err, result) => { 18 | expect(result).toBeInstanceOf(Twilio.twiml.MessagingResponse); 19 | done(); 20 | }; 21 | 22 | helloVoice(context, event, callback); 23 | }); 24 | 25 | test('says Hello World', (done) => { 26 | const callback = (_err, result) => { 27 | expect(result.toString()).toMatch('Hello World'); 28 | done(); 29 | }; 30 | 31 | helloVoice(context, event, callback); 32 | }); 33 | -------------------------------------------------------------------------------- /_helpers/template-docs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { templates } = require('../templates.json'); 3 | 4 | console.log('Generating templates.md'); 5 | 6 | const docHeader = `## Available Functions 7 | 8 | This is the list of Functions available in this repo:`; 9 | 10 | const baseUrl = 'https://github.com/twilio-labs/function-templates/blob/main'; 11 | const templateLine = (id, name, description) => { 12 | return `* [${name}](${baseUrl}/${id}): ${description}`; 13 | }; 14 | 15 | const templateList = templates.map(({ id, name, description }) => 16 | templateLine(id, name, description) 17 | ); 18 | 19 | const templateMarkdown = [docHeader, ...templateList].join('\n'); 20 | 21 | try { 22 | fs.writeFileSync('../docs/templates.md', templateMarkdown); 23 | console.log('file updated'); 24 | } catch (err) { 25 | console.error(err); 26 | } 27 | -------------------------------------------------------------------------------- /_helpers/test-suite/cypress.js: -------------------------------------------------------------------------------- 1 | const cypress = require('cypress'); 2 | 3 | async function runTests(cypressConfig) { 4 | return cypress.run(cypressConfig); 5 | } 6 | 7 | module.exports = { runTests }; 8 | -------------------------------------------------------------------------------- /_helpers/test-suite/index.js: -------------------------------------------------------------------------------- 1 | const { getConfig } = require('./e2e-config'); 2 | const { startLocalTestServer } = require('./serverless'); 3 | const { runTests } = require('./cypress'); 4 | 5 | async function runE2eTestSuite({ baseDir, env, cypressOptions } = {}) { 6 | const config = await getConfig( 7 | baseDir || process.cwd(), 8 | env || {}, 9 | cypressOptions 10 | ); 11 | 12 | try { 13 | console.log('>>> Start local server'); 14 | const localDevServer = await startLocalTestServer(config.serverless); 15 | console.log('>>> Run Cypress Tests'); 16 | const result = await runTests(config.cypress); 17 | console.log('>>> Shutdown local server'); 18 | localDevServer.close(); 19 | if (result.status === 'failed' || result.totalFailed > 0) { 20 | // eslint-disable-next-line no-process-exit 21 | process.exit(1); 22 | } 23 | } catch (err) { 24 | console.error(err); 25 | // eslint-disable-next-line no-process-exit 26 | process.exit(1); 27 | } 28 | } 29 | 30 | module.exports = { 31 | runE2eTestSuite, 32 | }; 33 | -------------------------------------------------------------------------------- /_helpers/test-suite/serverless.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-unresolved 2 | const { LocalDevelopmentServer } = require('@twilio/runtime-handler/dev'); 3 | 4 | function listen(app, port) { 5 | return new Promise((resolve) => { 6 | const server = app.listen(port, () => { 7 | resolve(server); 8 | }); 9 | }); 10 | } 11 | 12 | async function startLocalTestServer(serverlessConfig) { 13 | const functionsServer = new LocalDevelopmentServer( 14 | serverlessConfig.port, 15 | serverlessConfig 16 | ); 17 | 18 | return listen(functionsServer.getApp(), serverlessConfig.port); 19 | } 20 | 21 | module.exports = { startLocalTestServer }; 22 | -------------------------------------------------------------------------------- /ai-assistants-samples/.env.example: -------------------------------------------------------------------------------- 1 | # description: The ID of your AI Assistant (starts with ai_asst_) 2 | # format: text 3 | # required: false 4 | # configurable: false 5 | ASSISTANT_SID= 6 | 7 | # description: API key for Google Maps service 8 | # format: text 9 | # required: false 10 | # configurable: false 11 | GOOGLE_MAPS_API_KEY= 12 | 13 | # description: API key for OpenAI used in Internet Search tool 14 | # format: secret 15 | # required: false 16 | # configurable: false 17 | OPENAI_API_KEY= 18 | 19 | # description: API key for Exa used in Internet Search tool 20 | # format: text 21 | # required: false 22 | # configurable: false 23 | EXA_API_KEY= 24 | 25 | # description: Flex Workflow SID used for Flex Handoffs 26 | # format: text 27 | # required: false 28 | # configurable: false 29 | FLEX_WORKFLOW_SID= 30 | 31 | # description: Flex Workspace SID used for Flex Handoffs 32 | # format: text 33 | # required: false 34 | # configurable: false 35 | FLEX_WORKSPACE_SID= -------------------------------------------------------------------------------- /ai-assistants-samples/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | eshenfield 3 | -------------------------------------------------------------------------------- /ai-assistants-samples/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /ai-assistants-samples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-assistants-samples", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@langchain/community": "^0.0.32", 7 | "@langchain/core": "^0.1.32", 8 | "@langchain/openai": "^0.0.14", 9 | "@twilio/runtime-handler": "1.3.0", 10 | "exa-js": "^1.0.12", 11 | "jsonwebtoken": "^9.0.2", 12 | "langchain": "^0.1.21", 13 | "twilio": "^3.56" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/channels/conversations/flex-webchat.test.js: -------------------------------------------------------------------------------- 1 | describe('channels/conversations/flex-webchat', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/channels/conversations/messageAdded.test.js: -------------------------------------------------------------------------------- 1 | describe('channels/conversations/messageAdded', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/channels/conversations/response.test.js: -------------------------------------------------------------------------------- 1 | describe('channels/conversations/response', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/channels/messaging/incoming.test.js: -------------------------------------------------------------------------------- 1 | describe('channels/messaging/incoming', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/channels/messaging/response.test.js: -------------------------------------------------------------------------------- 1 | describe('channels/messaging/response', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/tools/flex-handover.test.js: -------------------------------------------------------------------------------- 1 | describe('tools/flex-handover', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/tools/google-maps.test.js: -------------------------------------------------------------------------------- 1 | describe('tools/google-maps', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/tools/studio-handover.test.js: -------------------------------------------------------------------------------- 1 | describe('tools/studio-handover', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /ai-assistants-samples/tests/tools/ui-tools.test.js: -------------------------------------------------------------------------------- 1 | describe('tools/ui-tools', () => { 2 | it('TODO', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /airtable/.env: -------------------------------------------------------------------------------- 1 | # description: Your Airtable API Key 2 | # format: secret 3 | # link: https://support.airtable.com/hc/en-us/articles/219046777-How-do-I-get-my-API-key- 4 | # required: true 5 | AIRTABLE_API_KEY= 6 | 7 | # description: Your Airtable Base ID 8 | # format: text 9 | # link: https://airtable.com/api 10 | # required: true 11 | AIRTABLE_BASE_ID=appAbcD12efG3HijK 12 | 13 | # description: Your Airtable Table Name 14 | # format: text 15 | # required: true 16 | AIRTABLE_TABLE_NAME=Table 1 17 | 18 | # description: The Twilio phone number to send broadcast SMS from 19 | # format: phone_number 20 | # required: true 21 | TWILIO_PHONE_NUMBER=+12223334444 22 | 23 | # description: The path to the webhook 24 | # configurable: false 25 | TWILIO_SMS_WEBHOOK_URL=/save-sms 26 | -------------------------------------------------------------------------------- /airtable/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /airtable/functions/broadcast-sms.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const response = new Twilio.Response(); 3 | 4 | const Airtable = require('airtable'); 5 | const base = new Airtable({ apiKey: context.AIRTABLE_API_KEY }).base( 6 | context.AIRTABLE_BASE_ID 7 | ); 8 | 9 | base 10 | .table(context.AIRTABLE_TABLE_NAME) 11 | .select() 12 | .all() 13 | .then((records) => { 14 | const sendingMessages = records.map((record) => { 15 | const client = context.getTwilioClient(); 16 | return client.messages 17 | .create({ 18 | to: record.get('From'), 19 | from: context.TWILIO_PHONE_NUMBER, 20 | body: 'This is a broadcast message from Twilio.', 21 | }) 22 | .then((msg) => { 23 | console.log(msg.sid); 24 | }) 25 | .catch((err) => { 26 | console.log(err); 27 | }); 28 | }); 29 | return Promise.all(sendingMessages); 30 | }) 31 | .then(() => { 32 | callback(null, response); 33 | }) 34 | .catch((err) => { 35 | callback(err); 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /airtable/functions/save-sms.protected.js: -------------------------------------------------------------------------------- 1 | const Airtable = require('airtable'); 2 | 3 | exports.handler = function (context, event, callback) { 4 | const twiml = new Twilio.twiml.MessagingResponse(); 5 | 6 | const base = new Airtable({ apiKey: context.AIRTABLE_API_KEY }).base( 7 | context.AIRTABLE_BASE_ID 8 | ); 9 | 10 | base 11 | .table(context.AIRTABLE_TABLE_NAME) 12 | .create({ 13 | Sid: event.MessageSid, 14 | From: event.From, 15 | Body: event.Body, 16 | }) 17 | .then((createdRecord) => { 18 | twiml.message('The SMS was successfully saved.'); 19 | callback(null, twiml); 20 | }) 21 | .catch((err) => { 22 | console.log(err); 23 | callback(err); 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /airtable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "airtable-sms-save", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "airtable": "^0.11.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /blank/.env.example: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/blank/.env.example -------------------------------------------------------------------------------- /blank/README.md: -------------------------------------------------------------------------------- 1 | # Blank 2 | 3 | This is a blank template Function that will return an empty JSON 4 | 5 | ## Environment variables 6 | 7 | This Function requires no environment variables to run successfully. 8 | 9 | ## Parameters 10 | 11 | This Function requires no URL or POST parameters to run successfully. 12 | -------------------------------------------------------------------------------- /blank/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /blank/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /blank/tests/blank.test.js: -------------------------------------------------------------------------------- 1 | const blank = require('../functions/blank').handler; 2 | 3 | describe('blank function template', () => { 4 | it('Calls callback with empty JSON', () => { 5 | const callback = jest.fn(); 6 | blank({}, {}, callback); 7 | expect(callback).toHaveBeenCalledWith(null, {}); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /block-spam-calls/.env: -------------------------------------------------------------------------------- 1 | # description: The path to the webhook 2 | # configurable: false 3 | TWILIO_VOICE_WEBHOOK_URL=/block-spam-calls 4 | -------------------------------------------------------------------------------- /block-spam-calls/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | 7 | ### Added 8 | 9 | - Initial release. 10 | -------------------------------------------------------------------------------- /block-spam-calls/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "block-spam-calls", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /block-spam-calls/tests/spam-filter-results/clean-marchex.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "successful", 3 | "message": null, 4 | "code": null, 5 | "results": { 6 | "marchex_cleancall": { 7 | "request_sid": "XRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 8 | "status": "successful", 9 | "message": null, 10 | "code": null, 11 | "result": { 12 | "result": { 13 | "recommendation": "PASS", 14 | "reason": "CleanCall" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /block-spam-calls/tests/spam-filter-results/clean-nomorobo.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "successful", 3 | "message": null, 4 | "code": null, 5 | "results": { 6 | "nomorobo_spamscore": { 7 | "request_sid": "XRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 8 | "status": "successful", 9 | "message": null, 10 | "code": null, 11 | "result": { 12 | "status": "success", 13 | "message": "success", 14 | "score": 0 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /block-spam-calls/tests/spam-filter-results/failed-nomorobo.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "successful", 3 | "message": null, 4 | "code": null, 5 | "results": { 6 | "nomorobo_spamscore": { 7 | "request_sid": "XRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 8 | "status": "failed", 9 | "message": "Vendor could not complete request", 10 | "code": 61002, 11 | "result": {} 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /block-spam-calls/tests/spam-filter-results/spam-marchex.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "successful", 3 | "message": null, 4 | "code": null, 5 | "results": { 6 | "marchex_cleancall": { 7 | "request_sid": "XRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 8 | "status": "successful", 9 | "message": null, 10 | "code": null, 11 | "result": { 12 | "result": { 13 | "recommendation": "BLOCK", 14 | "reason": "Testing" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /block-spam-calls/tests/spam-filter-results/spam-nomorobo.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "successful", 3 | "message": null, 4 | "code": null, 5 | "results": { 6 | "nomorobo_spamscore": { 7 | "request_sid": "XRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 8 | "status": "successful", 9 | "message": null, 10 | "code": null, 11 | "result": { 12 | "status": "success", 13 | "message": "success", 14 | "score": 1 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /blocklist-call/.env: -------------------------------------------------------------------------------- 1 | # description: Only phone numbers in this comma-separated list will be allowed to join the conference. All phone numbers must follow the E.164 format 2 | # format: list(phone_number) 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | BLOCKLIST="+12125551234,+17025556789" 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/blocklist-call -------------------------------------------------------------------------------- /blocklist-call/README.md: -------------------------------------------------------------------------------- 1 | # Twilio Blocklist Call 2 | 3 | This function in `blocklist-call.js` allows users to setup a blocklist to reject unwanted phone numbers. See [here](https://support.twilio.com/hc/en-us/articles/360034788313-Reject-Incoming-Calls-with-a-Phone-Number-Blacklist) for a more in-depth guide. 4 | 5 | ### Environment variables 6 | 7 | This Function expects one environment variable to be set. 8 | 9 | | Variable | Meaning | 10 | | :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | `BLOCKLIST` | The comma-separated list of numbers you want to reject [in E.164 format](https://support.twilio.com/hc/en-us/articles/223183008-Formatting-International-Phone-Numbers) | 12 | 13 | ### Parameters 14 | 15 | This Function expects the incoming request to be a voice webhook. The parameters that will be used are `From` and `blocklist`. 16 | -------------------------------------------------------------------------------- /blocklist-call/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /blocklist-call/functions/blocklist-call.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const blocklist = event.blocklist || context.BLOCKLIST || ''; 3 | const blocklistArray = blocklist 4 | .toString() 5 | .split(',') 6 | .map((num) => num.trim()); 7 | const twiml = new Twilio.twiml.VoiceResponse(); 8 | 9 | let blocked = false; 10 | if (blocklistArray.length > 0 && blocklistArray.includes(event.From)) { 11 | blocked = true; 12 | } 13 | 14 | if (blocked) { 15 | twiml.reject(); 16 | } else { 17 | // Update this line to your response. 18 | twiml.redirect( 19 | { 20 | method: 'GET', 21 | }, 22 | 'https://demo.twilio.com/docs/voice.xml' 23 | ); 24 | } 25 | callback(null, twiml); 26 | }; 27 | -------------------------------------------------------------------------------- /blocklist-call/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /caller-id-forwarding/.env: -------------------------------------------------------------------------------- 1 | # Variables for function "" 2 | # --- 3 | # description: Calls made to your Twilio number will get forwarded to this e164-formatted phone number 4 | # format: phone_number 5 | # required: true 6 | # link: https://www.twilio.com/docs/glossary/what-e164 7 | MY_PHONE_NUMBER=+12223334444 8 | 9 | # description: The path to the webhook 10 | # configurable: false 11 | TWILIO_VOICE_WEBHOOK_URL=/forward-call -------------------------------------------------------------------------------- /caller-id-forwarding/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /caller-id-forwarding/functions/forward-call.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | try { 3 | const client = context.getTwilioClient(); 4 | 5 | const lookup = await client.lookups.v1 6 | .phoneNumbers(event.From) 7 | .fetch({ type: ['carrier', 'caller-name'] }); 8 | 9 | const callerName = lookup.callerName.caller_name; 10 | const carrierName = lookup.carrier.name; 11 | 12 | const body = `Incoming call from ${event.From} 13 | Name: ${callerName === null ? 'Unknown' : callerName} 14 | Carrier: ${carrierName} (${lookup.carrier.type})`; 15 | 16 | await client.messages.create({ 17 | body, 18 | from: context.TWILIO_PHONE_NUMBER, 19 | to: context.MY_PHONE_NUMBER, 20 | }); 21 | } catch (error) { 22 | console.error(error); 23 | } finally { 24 | // forward the call even if caller ID fails 25 | const twiml = new Twilio.twiml.VoiceResponse(); 26 | twiml.dial(context.MY_PHONE_NUMBER); 27 | return callback(null, twiml); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /caller-id-forwarding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chat-token/.env: -------------------------------------------------------------------------------- 1 | # description: API key for your Twilio Account 2 | # format: text 3 | # link: https://www.twilio.com/console/runtime/api-keys/create 4 | # required: true 5 | API_KEY= 6 | 7 | # description: API secret for your API Key 8 | # format: secret 9 | # link: https://www.twilio.com/console/runtime/api-keys/create 10 | # required: true 11 | API_SECRET= 12 | 13 | # description: SID of your Twilio Chat Service 14 | # format: sid 15 | # link: https://www.twilio.com/docs/api/chat/rest/services 16 | # required: true 17 | CHAT_SERVICE_SID= -------------------------------------------------------------------------------- /chat-token/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /chat-token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /conference-caller-gated/.env: -------------------------------------------------------------------------------- 1 | # description: Phone number of the moderator. Conference starts when this number joins. In E.164 format 2 | # format: phone_number 3 | # required: true 4 | MODERATOR_PHONE_NUMBER= 5 | 6 | # description: Only phone numbers in this comma-separated list will be allowed to join the conference. All phone numbers must follow the E.164 format 7 | # format: list(phone_number) 8 | # required: true 9 | # link: https://www.twilio.com/docs/glossary/what-e164 10 | VALID_PARTICIPANTS= 11 | 12 | # description: The path to the webhook 13 | # configurable: false 14 | TWILIO_VOICE_WEBHOOK_URL=/gated-conference -------------------------------------------------------------------------------- /conference-caller-gated/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | # Insert your Github username here 5 | -------------------------------------------------------------------------------- /conference-caller-gated/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /conference-caller-gated/functions/gated-conference.protected.js: -------------------------------------------------------------------------------- 1 | function rejectCaller() { 2 | const twiml = new Twilio.twiml.VoiceResponse(); 3 | twiml.say(`I'm sorry but I don't recognize your number. Have a good day.`); 4 | twiml.hangup(); 5 | return twiml; 6 | } 7 | 8 | function joinConference(isModerator) { 9 | const twiml = new Twilio.twiml.VoiceResponse(); 10 | twiml.say('Thank you! You are joining the conference'); 11 | twiml.dial().conference( 12 | { 13 | startConferenceOnEnter: isModerator, 14 | endConferenceOnExit: isModerator, 15 | }, 16 | 'My gated conference' 17 | ); 18 | return twiml; 19 | } 20 | 21 | exports.handler = function (context, event, callback) { 22 | const validParticipants = context.VALID_PARTICIPANTS.split(',').map((num) => 23 | num.trim() 24 | ); 25 | 26 | const isValidParticipant = validParticipants.includes(event.From); 27 | const isModerator = event.From === context.MODERATOR_PHONE_NUMBER.trim(); 28 | 29 | let twiml; 30 | 31 | if (!isValidParticipant && !isModerator) { 32 | twiml = rejectCaller(); 33 | } else { 34 | twiml = joinConference(isModerator); 35 | } 36 | callback(null, twiml); 37 | }; 38 | -------------------------------------------------------------------------------- /conference-caller-gated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conference-caller-gated", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /conference-pin/.env: -------------------------------------------------------------------------------- 1 | # description: Choose a PIN code (only digits) for your app. Callers have to enter this code to be connected to your conference line 2 | # format: integer 3 | # required: true 4 | CONFERENCE_PIN= 5 | 6 | # description: The path to the webhook 7 | # configurable: false 8 | TWILIO_VOICE_WEBHOOK_URL=/conference-with-pin -------------------------------------------------------------------------------- /conference-pin/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | # Insert your Github username here 5 | -------------------------------------------------------------------------------- /conference-pin/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /conference-pin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conference-pin", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /conference-verify/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | VERIFY_SERVICE_SID= 5 | 6 | # description: Phone number of the moderator. Conference starts when this number joins. In E.164 format 7 | # format: phone_number 8 | # required: true 9 | MODERATOR_PHONE_NUMBER= 10 | 11 | # description: Only phone numbers in this comma-separated list will be allowed to join the conference. All phone number must use E.164 format 12 | # format: list(phone_number) 13 | # required: true 14 | VALID_PARTICIPANTS= 15 | 16 | # description: The path to the webhook 17 | # configurable: false 18 | TWILIO_VOICE_WEBHOOK_URL=/verify-conference -------------------------------------------------------------------------------- /conference-verify/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /conference-verify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conference-verify", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /conference/.env: -------------------------------------------------------------------------------- 1 | # description: The path to the webhook 2 | # configurable: false 3 | TWILIO_VOICE_WEBHOOK_URL=/conference -------------------------------------------------------------------------------- /conference/README.md: -------------------------------------------------------------------------------- 1 | # Conference 2 | 3 | This Function in `conference.js` will return the TwiML required to put a call into a conference. The conference is called "Snowy Owl" by default. 4 | 5 | ### Environment variables 6 | 7 | This Function doesn't need any environment variables. 8 | 9 | ### Parameters 10 | 11 | This Function expects the incoming request to be a voice webhook. It doesn't expect any incoming parameters. 12 | -------------------------------------------------------------------------------- /conference/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /conference/functions/conference.protected.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Conference Template 3 | * 4 | * This Function creates a conference line that people can call-in to. Learn more about using here: 5 | * https://www.twilio.com/docs/api/twiml/conference 6 | */ 7 | 8 | exports.handler = function (context, event, callback) { 9 | const twiml = new Twilio.twiml.VoiceResponse(); 10 | // Change the conference name to anything you like. 11 | const conferenceName = 'Snowy Owl'; 12 | twiml.dial().conference(conferenceName); 13 | callback(null, twiml); 14 | }; 15 | -------------------------------------------------------------------------------- /conference/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /conference/tests/conference.test.js: -------------------------------------------------------------------------------- 1 | const conference = require('../functions/conference.protected').handler; 2 | const helpers = require('../../test/test-helper'); 3 | 4 | describe('simple conference', () => { 5 | const context = {}; 6 | const event = {}; 7 | beforeAll(() => { 8 | helpers.setup(context); 9 | }); 10 | 11 | afterAll(() => { 12 | helpers.teardown(); 13 | }); 14 | 15 | it('returns a VoiceResponse', (done) => { 16 | const callback = (_err, result) => { 17 | expect(result).toBeInstanceOf(Twilio.twiml.VoiceResponse); 18 | done(); 19 | }; 20 | 21 | conference(context, event, callback); 22 | }); 23 | 24 | it('connects a call to a conference called Snowy Owl', (done) => { 25 | const callback = (_err, result) => { 26 | expect(result.toString()).toMatch( 27 | 'Snowy Owl' 28 | ); 29 | done(); 30 | }; 31 | 32 | conference(context, event, callback); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /contact-form/.env: -------------------------------------------------------------------------------- 1 | # description: Your SendGrid API key 2 | # format: secret 3 | # link: https://sendgrid.com/docs/ui/account-and-settings/api-keys/ 4 | # required: true 5 | SENDGRID_API_KEY= 6 | 7 | # description: Messages submitted through the contact form will be sent to this email address 8 | # format: email 9 | # required: true 10 | TO_EMAIL_ADDRESS= 11 | 12 | # description: Messages submitted through the contact form will be sent from this email address 13 | # format: email 14 | # required: true 15 | FROM_EMAIL_ADDRESS= 16 | -------------------------------------------------------------------------------- /contact-form/assets/style.css: -------------------------------------------------------------------------------- 1 | main { 2 | padding-top: 40px; 3 | display: flex; 4 | flex-direction: column; 5 | flex: 1; 6 | justify-content: flex-start; 7 | width: 75%; 8 | margin-left: auto; 9 | margin-right: auto; 10 | } 11 | 12 | div.content { 13 | margin: 0 auto; 14 | width: 100%; 15 | max-width: 800px; 16 | } 17 | 18 | label { 19 | display: block; 20 | } 21 | 22 | input, 23 | textarea { 24 | box-sizing: border-box; 25 | font: inherit; 26 | border: 1px solid rgb(136, 145, 170); 27 | border-radius: 4px; 28 | padding: 0.6rem; 29 | width: 50%; 30 | } 31 | 32 | @media screen and (max-width: 640px) { 33 | input, 34 | input[type='text'], 35 | textarea { 36 | width: 100%; 37 | } 38 | } 39 | 40 | .form-field { 41 | padding: 1rem 0; 42 | } 43 | 44 | .status { 45 | color: #fff; 46 | padding: 0.6rem; 47 | border-radius: 4px; 48 | } 49 | 50 | .status.success { 51 | background-color: rgb(20, 176, 83); 52 | } 53 | 54 | .status.error { 55 | background-color: rgb(214, 31, 31); 56 | } 57 | -------------------------------------------------------------------------------- /contact-form/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | -------------------------------------------------------------------------------- /contact-form/contact-form.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/contact-form/contact-form.gif -------------------------------------------------------------------------------- /contact-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contact-form", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@sendgrid/mail": "^7.6.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /conversations/.env: -------------------------------------------------------------------------------- 1 | # description: The SID of the Studio Flow you want to use Conversations with. This number is found on your Studio Dashboard 2 | # format: sid 3 | # link: https://www.twilio.com/console/studio/dashboard 4 | # required: true 5 | STUDIO_FLOW_SID=FWxxxxxxxxx -------------------------------------------------------------------------------- /conversations/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /conversations/functions/generate-scoped-webhook.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const client = context.getTwilioClient(); 3 | 4 | client.conversations 5 | .conversations(event.ConversationSid) 6 | .webhooks.create({ 7 | 'configuration.flowSid': context.STUDIO_FLOW_SID, 8 | 'configuration.replayAfter': 0, 9 | target: 'studio', 10 | }) 11 | .then((webhook) => { 12 | const response = `webhook ${webhook.sid} successfully created`; 13 | callback(null, response); 14 | }) 15 | .catch((err) => { 16 | console.log(err); 17 | callback(err); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /conversations/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conversations", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "twilio": "^3.61.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | # Insert your Github username here 5 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/assets/prebuilt-chatbots/india_mohfw_agent.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/covid-vaccine-faq-bot/assets/prebuilt-chatbots/india_mohfw_agent.zip -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/assets/prebuilt-chatbots/usa_ama_agent.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/covid-vaccine-faq-bot/assets/prebuilt-chatbots/usa_ama_agent.zip -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/assets/service-account-key.private.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "", 4 | "private_key_id": "", 5 | "private_key": "", 6 | "client_email": "", 7 | "client_id": "", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "" 12 | } 13 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/functions/check-existing-flow.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const client = context.getTwilioClient(); 3 | const flowSid = context.FLOW_SID; 4 | 5 | client.studio.flows 6 | .list({ limit: 100 }) 7 | .then((flows) => { 8 | if (flows.length > 0) { 9 | return flows.forEach((f) => { 10 | /* 11 | * Note: If you are running this app locally and you kill your dev server after creating 12 | * the Studio Flow via the app, you will need to manually set FLOW_SID in your .env file 13 | * before the next time you start your dev server. 14 | */ 15 | if (f.sid === flowSid) { 16 | return callback(null, f.sid); 17 | } 18 | return callback(null, 'none'); 19 | }); 20 | } 21 | 22 | return callback(null, 'none'); 23 | }) 24 | .catch((err) => callback(err)); 25 | }; 26 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/functions/return-config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line func-names 2 | exports.handler = function (context, event, callback) { 3 | const phoneNumber = context.TWILIO_PHONE_NUMBER; 4 | // eslint-disable-next-line no-undef 5 | const response = new Twilio.Response(); 6 | response.setStatusCode(200); 7 | response.appendHeader('Content-Type', 'application/json'); 8 | response.setBody({ phoneNumber }); 9 | callback(null, response); 10 | }; 11 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "covid-vaccine-faq-bot", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@google-cloud/dialogflow": "^4.1.0", 7 | "crypto": "^1.0.1", 8 | "twilio": "^3.64.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/tests/check-googlejson-config.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const checkConfig = require('../functions/check-googlejson-config').handler; 3 | 4 | const event = {}; 5 | 6 | const context = { 7 | GOOGLE_APPLICATION_CREDENTIALS: '/service-account-key.json', 8 | }; 9 | 10 | beforeAll(() => { 11 | const runtime = new helpers.MockRuntime(); 12 | runtime._addAsset( 13 | '/service-account-key.json', 14 | './service-account-key.test.json' 15 | ); 16 | helpers.setup(context, runtime); 17 | }); 18 | 19 | afterAll(() => { 20 | helpers.teardown(); 21 | }); 22 | 23 | it('should return a success message for a valid configuration', (done) => { 24 | const callback = (err, res) => { 25 | expect(err).toBeFalsy(); 26 | expect(res._body.success).toBeTruthy(); 27 | expect(res._body.message).toMatch( 28 | 'Google service account key is configured properly.' 29 | ); 30 | done(); 31 | }; 32 | 33 | checkConfig(context, event, callback); 34 | }); 35 | -------------------------------------------------------------------------------- /covid-vaccine-faq-bot/tests/service-account-key.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "test project ID", 4 | "private_key_id": "test private key ID", 5 | "private_key": "test private key", 6 | "client_email": "test@example.org", 7 | "client_id": "test client ID", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://example.org/test" 12 | } 13 | -------------------------------------------------------------------------------- /docs/images/APP_INFO_V2_comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/APP_INFO_V2_comment.png -------------------------------------------------------------------------------- /docs/images/EDIT_CODE_comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/EDIT_CODE_comment.png -------------------------------------------------------------------------------- /docs/images/add-dependencies-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/add-dependencies-v2.png -------------------------------------------------------------------------------- /docs/images/add-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/add-function.png -------------------------------------------------------------------------------- /docs/images/create-env-variables-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/create-env-variables-v2.png -------------------------------------------------------------------------------- /docs/images/download_asset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/images/download_asset.png -------------------------------------------------------------------------------- /docs/static/v1/Inter.var.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/static/v1/Inter.var.woff2 -------------------------------------------------------------------------------- /docs/static/v1/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/static/v1/favicon.ico -------------------------------------------------------------------------------- /docs/static/v1/video_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/docs/static/v1/video_logo.png -------------------------------------------------------------------------------- /experimental-flex-dialpad/.env.example: -------------------------------------------------------------------------------- 1 | # description: SID of the TaskRouter workflow used for transfers 2 | # configurable: false 3 | TWILIO_WORKFLOW_SID= 4 | 5 | # description: SID for the TaskRouter Workspace that contains your workflow 6 | # configurable: false 7 | TWILIO_WORKSPACE_SID= 8 | 9 | # description: The phone number used for your transfers 10 | TWILIO_PHONE_NUMBER= -------------------------------------------------------------------------------- /experimental-flex-dialpad/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/assets/flex-dialpad-addon.js.private.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"flex-dialpad-addon.js","sourceRoot":""} -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/dialpad-utils.js: -------------------------------------------------------------------------------- 1 | exports.response = (format, body) => { 2 | // Create a custom Twilio Response 3 | // Set the CORS headers to allow Flex to make an HTTP request to the Twilio Function 4 | const response = new Twilio.Response(); 5 | response.appendHeader('Access-Control-Allow-Origin', '*'); 6 | response.appendHeader('Access-Control-Allow-Methods', 'OPTIONS POST GET'); 7 | response.appendHeader('Access-Control-Allow-Headers', 'Content-Type'); 8 | 9 | if (format === 'json') { 10 | response.appendHeader('Content-Type', 'application/json'); 11 | response.setBody(body); 12 | } 13 | 14 | return response; 15 | }; 16 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/external-transfer/add-conference-participant.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const { taskSid, to, from } = event; 8 | 9 | console.log(`Adding ${to} to named conference ${taskSid}`); 10 | 11 | const client = context.getTwilioClient(); 12 | 13 | const participantsResponse = await client 14 | .conferences(taskSid) 15 | .participants.create({ 16 | to, 17 | from, 18 | earlyMedia: true, 19 | endConferenceOnExit: false, 20 | }); 21 | 22 | console.log('Participant response properties:'); 23 | 24 | Object.keys(participantsResponse).forEach((key) => { 25 | console.log(`${key}: ${participantsResponse[key]}`); 26 | }); 27 | 28 | callback(null, assets.response('json', participantsResponse)); 29 | }); 30 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/external-transfer/get-call-properties.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const { callSid } = event; 8 | 9 | console.log(`Getting properties for call SID ${callSid}`); 10 | const client = context.getTwilioClient(); 11 | 12 | const callProperties = await client.calls(callSid).fetch(); 13 | 14 | console.log('Call properties:'); 15 | 16 | Object.keys(callProperties).forEach((key) => { 17 | console.log(`${key}: ${callProperties[key]}`); 18 | }); 19 | 20 | callback(null, assets.response('json', callProperties)); 21 | }); 22 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/external-transfer/hold-conference-participant.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const { conference, participant, hold } = event; 8 | 9 | console.log( 10 | `${hold ? 'Holding' : 'Unholding'} participant ${participant} ` + 11 | `in conference ${conference}` 12 | ); 13 | 14 | const client = context.getTwilioClient(); 15 | 16 | const participantsResponse = await client 17 | .conferences(conference) 18 | .participants(participant) 19 | .update({ 20 | hold, 21 | }); 22 | 23 | console.log(`Participant ${participant} updated in conference \ 24 | ${conference}. Participant response properties:`); 25 | 26 | Object.keys(participantsResponse).forEach((key) => { 27 | console.log(` ${key}:`, participantsResponse[key]); 28 | }); 29 | 30 | callback(null, assets.response('json', participantsResponse)); 31 | }); 32 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/external-transfer/remove-conference-participant.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const { conference, participant } = event; 8 | 9 | console.log( 10 | `Removing participant ${participant} from conference ${conference}` 11 | ); 12 | 13 | const client = context.getTwilioClient(); 14 | 15 | const participantResponse = await client 16 | .conferences(conference) 17 | .participants(participant) 18 | .remove(); 19 | 20 | console.log('Participant response properties:'); 21 | 22 | Object.keys(participantResponse).forEach((key) => { 23 | console.log(`${key}: ${participantResponse[key]}`); 24 | }); 25 | 26 | return callback(null, assets.response('json', participantResponse)); 27 | }); 28 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/external-transfer/update-conference-participant.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const { conference, participant, endConferenceOnExit } = event; 8 | 9 | console.log( 10 | `Updating participant ${participant} in conference ${conference}` 11 | ); 12 | 13 | const client = context.getTwilioClient(); 14 | 15 | const participantResponse = await client 16 | .conferences(conference) 17 | .participants(participant) 18 | .update({ 19 | endConferenceOnExit, 20 | }) 21 | .catch((e) => { 22 | console.error(e); 23 | return {}; 24 | }); 25 | 26 | console.log('Participant response properties:'); 27 | 28 | Object.keys(participantResponse).forEach((key) => { 29 | console.log(`${key}: ${participantResponse[key]}`); 30 | }); 31 | 32 | return callback(null, assets.response('json', participantResponse)); 33 | }); 34 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/internal-call/agent-join-conference.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = (context, event, callback) => { 2 | let twiml = new Twilio.twiml.VoiceResponse(); 3 | 4 | twiml.dial().conference( 5 | { 6 | endConferenceOnExit: true, 7 | }, 8 | event.conferenceName 9 | ); 10 | 11 | callback(null, twiml); 12 | }; 13 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/internal-call/agent-outbound-join.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = (context, event, callback) => { 2 | let twiml = new Twilio.twiml.VoiceResponse(); 3 | 4 | twiml.dial().conference( 5 | { 6 | statusCallback: 'call-outbound-join', 7 | statusCallbackEvent: 'join end', 8 | endConferenceOnExit: true, 9 | }, 10 | event.taskSid 11 | ); 12 | 13 | callback(null, twiml); 14 | }; 15 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/internal-call/cleanup-rejected-task.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | const taskSid = event.taskSid; 8 | 9 | let client = context.getTwilioClient(); 10 | 11 | const conferences = await client.conferences.list({ 12 | friendlyName: taskSid, 13 | status: 'in-progress', 14 | limit: 20, 15 | }); 16 | 17 | await Promise.all( 18 | conferences.map((conference) => { 19 | return client.conferences(conference.sid).update({ status: 'completed' }); 20 | }) 21 | ); 22 | 23 | callback(null, assets.response('json', {})); 24 | }); 25 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/functions/internal-call/hold-call.js: -------------------------------------------------------------------------------- 1 | const TokenValidator = require('twilio-flex-token-validator').functionValidator; 2 | 3 | let path = Runtime.getFunctions()['dialpad-utils'].path; 4 | let assets = require(path); 5 | 6 | exports.handler = TokenValidator(async (context, event, callback) => { 7 | console.log('passei por aqui'); 8 | 9 | const client = context.getTwilioClient(); 10 | 11 | console.log(event); 12 | 13 | const { conference, participant, hold } = event; 14 | 15 | console.log(conference); 16 | console.log(participant); 17 | console.log(hold); 18 | 19 | if (conference && participant && hold) { 20 | console.log('entrei aqui'); 21 | 22 | await client.conferences(conference).participants(participant).update({ 23 | hold, 24 | }); 25 | } 26 | 27 | callback(null, assets.response('json', {})); 28 | }); 29 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flex-dialpad", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@twilio-labs/runtime-helpers": "^0.1.2", 7 | "@twilio-labs/serverless-api": "^5.5.0", 8 | "@twilio-labs/serverless-runtime-types": "^2.2.3", 9 | "axios": "^1.6.4", 10 | "twilio": "^4.19.3", 11 | "twilio-flex-token-validator": "^1.5.1" 12 | }, 13 | "scripts": { 14 | "build": "cd ui-src && twilio flex:plugins:build -p flex-plugins && cp build/** ../assets" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/tests/setup.test.js: -------------------------------------------------------------------------------- 1 | // const { handler } = require('../functions/setup'); 2 | 3 | describe('flex-dialpad function template', () => { 4 | it('TODO Placeholder', () => { 5 | expect(true).toBeTruthy(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_SERVICE_BASE_URL= 2 | REACT_APP_TASK_CHANNEL_SID= -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (config, { isProd, isDev, isTest }) => { 2 | /** 3 | * Customize the Jest by modifying the config object. 4 | * Consult https://jestjs.io/docs/en/configuration for more information. 5 | */ 6 | 7 | return config; 8 | }; 9 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flex-dialpad-addon", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "prebuild": "rimraf build && npm run bootstrap", 7 | "postinstall": "flex-plugin pre-script-check", 8 | "prestart": "npm run bootstrap" 9 | }, 10 | "dependencies": { 11 | "@emotion/core": "^10.0.28", 12 | "@emotion/styled": "^10.0.27", 13 | "lodash": "^4.17.20", 14 | "react": "16.5.2", 15 | "react-dom": "16.5.2", 16 | "react-select": "^3.1.0", 17 | "@twilio/flex-plugin-scripts": "7.0.0" 18 | }, 19 | "devDependencies": { 20 | "@twilio/flex-ui": "^1", 21 | "react-test-renderer": "16.5.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/public/appConfig.example.js: -------------------------------------------------------------------------------- 1 | var appConfig = { 2 | pluginService: { 3 | enabled: true, 4 | url: '/plugins', 5 | }, 6 | ytica: false, 7 | logLevel: 'info', 8 | showSupervisorDesktopView: true, 9 | }; 10 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/screenshots/outbound-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/experimental-flex-dialpad/ui-src/screenshots/outbound-filter.png -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/DialpadPlugin.js: -------------------------------------------------------------------------------- 1 | import { FlexPlugin } from '@twilio/flex-plugin'; 2 | 3 | import registerCustomActions from './customActions'; 4 | import registerCustomNotifications from './notifications'; 5 | import { loadExternalTransferInterface } from './components/ExternalTransfer'; 6 | import { loadInternalCallInterface } from './components/InternalCall'; 7 | 8 | const PLUGIN_NAME = 'DialpadPlugin'; 9 | 10 | export default class DialpadPlugin extends FlexPlugin { 11 | constructor() { 12 | super(PLUGIN_NAME); 13 | } 14 | 15 | init(flex, manager) { 16 | loadExternalTransferInterface.bind(this)(flex, manager); 17 | 18 | loadInternalCallInterface.bind(this)(flex, manager); 19 | 20 | registerCustomActions(manager); 21 | registerCustomNotifications(flex, manager); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/components/ExternalTransfer/ConferenceButton.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | Actions, 4 | IconButton, 5 | TaskHelper, 6 | withTheme, 7 | Manager, 8 | } from '@twilio/flex-ui'; 9 | 10 | class ConferenceButton extends React.PureComponent { 11 | handleClick = () => { 12 | Actions.invokeAction('SetComponentState', { 13 | name: 'ConferenceDialog', 14 | state: { isOpen: true }, 15 | }); 16 | }; 17 | 18 | render() { 19 | const isLiveCall = TaskHelper.isLiveCall(this.props.task); 20 | 21 | return ( 22 | 23 | 30 | 31 | ); 32 | } 33 | } 34 | 35 | export default withTheme(ConferenceButton); 36 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/components/ExternalTransfer/ParticipantStatusContainer.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from '@emotion/styled'; 3 | import { withTheme } from '@twilio/flex-ui'; 4 | import ParticipantName from './ParticipantName'; 5 | import ParticipantStatus from './ParticipantStatus'; 6 | 7 | const StatusContainer = styled('div')` 8 | display: flex; 9 | flex-wrap: nowrap; 10 | flex-grow: 1; 11 | flex-shrink: 1; 12 | flex-direction: column; 13 | overflow: hidden; 14 | `; 15 | 16 | class ParticipantStatusContainer extends React.PureComponent { 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | 23 | ); 24 | } 25 | } 26 | 27 | export default withTheme(ParticipantStatusContainer); 28 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/customActions/externalTransfer/index.js: -------------------------------------------------------------------------------- 1 | import ConferenceService from '../../helpers/ConferenceService'; 2 | 3 | export const kickExternalTransferParticipant = (payload) => { 4 | const { task, targetSid } = payload; 5 | 6 | const conference = task.attributes.conference 7 | ? task.attributes.conference.sid 8 | : task.conference.conferenceSid; 9 | 10 | const participantSid = targetSid; 11 | 12 | console.log(`Removing participant ${participantSid} from conference`); 13 | return ConferenceService.removeParticipant(conference, participantSid); 14 | }; 15 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/helpers/request.js: -------------------------------------------------------------------------------- 1 | const request = async (path, manager, params) => { 2 | const body = { 3 | ...params, 4 | Token: manager.store.getState().flex.session.ssoTokenPayload.token, 5 | }; 6 | 7 | const options = { 8 | method: 'POST', 9 | body: new URLSearchParams(body), 10 | headers: { 11 | 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 12 | }, 13 | }; 14 | 15 | const { REACT_APP_SERVICE_BASE_URL } = process.env; 16 | console.log('REQUEST BASE URL: ', REACT_APP_SERVICE_BASE_URL, ' PATH:', path); 17 | const resp = await fetch(`${REACT_APP_SERVICE_BASE_URL}/${path}`, options); 18 | return await resp.json(); 19 | }; 20 | 21 | export { request }; 22 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/index.js: -------------------------------------------------------------------------------- 1 | import * as FlexPlugin from '@twilio/flex-plugin'; 2 | import DialpadPlugin from './DialpadPlugin'; 3 | 4 | FlexPlugin.loadPlugin(DialpadPlugin); 5 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/notifications/index.js: -------------------------------------------------------------------------------- 1 | import * as Flex from '@twilio/flex-ui'; 2 | 3 | export default (flex, manager) => { 4 | registerCustomNotifications(flex, manager); 5 | }; 6 | 7 | export const CustomNotifications = { 8 | FailedHangupNotification: 'PS_FailedHangupOnConferenceWithExternalParties', 9 | }; 10 | 11 | function registerCustomNotifications(flex, manager) { 12 | flex.Notifications.registerNotification({ 13 | id: CustomNotifications.FailedHangupNotification, 14 | type: Flex.NotificationType.error, 15 | content: 16 | 'Hangup call abandoned: Failed to take all participants off hold while hanging up the call. If this issue persists, please try unholding participants manually before leaving the call', 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/src/setupTests.js: -------------------------------------------------------------------------------- 1 | require('babel-polyfill'); 2 | 3 | import { configure } from 'enzyme/build'; 4 | import Adapter from 'enzyme-adapter-react-16/build'; 5 | 6 | configure({ 7 | adapter: new Adapter(), 8 | }); 9 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (config, { isProd, isDev, isTest }) => { 2 | /** 3 | * Customize the webpack by modifying the config object. 4 | * Consult https://webpack.js.org/configuration for more information 5 | */ 6 | 7 | return config; 8 | }; 9 | -------------------------------------------------------------------------------- /experimental-flex-dialpad/ui-src/webpack.dev.js: -------------------------------------------------------------------------------- 1 | module.exports = (config, { isProd, isDev, isTest }) => { 2 | /** 3 | * Customize the webpack dev-server by modifying the config object. 4 | * Consult https://webpack.js.org/configuration/dev-server for more information. 5 | */ 6 | 7 | return config; 8 | }; 9 | -------------------------------------------------------------------------------- /forward-call/.env: -------------------------------------------------------------------------------- 1 | # description: Calls made to your Twilio number will get forwarded to this e164-formatted phone number 2 | # format: phone_number 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | MY_PHONE_NUMBER=+12223334444 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/forward-call -------------------------------------------------------------------------------- /forward-call/README.md: -------------------------------------------------------------------------------- 1 | # Forward Call 2 | 3 | This Function in `forward-call.js` will return the [TwiML](https://www.twilio.com/docs/voice/twiml) required to forward an incoming call to a number that is set in the environment variables. 4 | 5 | ### Environment variables 6 | 7 | This Function expects one environment variable to be set. 8 | 9 | | Variable | Meaning | 10 | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | `MY_PHONE_NUMBER` | The number you want to forward incoming calls to [in E.164 format](https://help.twilio.com/articles/223183008-Formatting-International-Phone-Numbers) | 12 | 13 | ### Parameters 14 | 15 | This Function expects the incoming request to be a messaging webhook. The parameters that will be used are `From` and `Body`. 16 | -------------------------------------------------------------------------------- /forward-call/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /forward-call/functions/forward-call.protected.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forward an Incoming Call to a Specified Number 3 | * Description: When your Twilio Phone Number associated with this function 4 | * receives a call, it will forward the call to MY_PHONE_NUMBER 5 | * specified in /.env 6 | * 7 | * Contents: 8 | * 1. Main Handler 9 | */ 10 | 11 | /* 12 | * 1. Main Handler 13 | * 14 | * This is the entry point to your Twilio Function, 15 | * which will create a new Voice Response using Twiml 16 | * and use this to dial the MY_PHONE_NUMBER 17 | * specified in /.env 18 | * We then use the callback to return from your function 19 | * with the Twiml Voice Response you defined earlier. 20 | * In the callback in non-error situations, the first 21 | * parameter is null and the second parameter 22 | * is the value you want to return. 23 | */ 24 | 25 | exports.handler = function (context, event, callback) { 26 | const twiml = new Twilio.twiml.VoiceResponse(); 27 | twiml.dial(context.MY_PHONE_NUMBER); 28 | callback(null, twiml); 29 | }; 30 | -------------------------------------------------------------------------------- /forward-call/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /forward-call/tests/forward-call.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const forwardCall = require('../functions/forward-call.protected').handler; 3 | const Twilio = require('twilio'); 4 | 5 | const context = { 6 | MY_PHONE_NUMBER: 'TwilioNumber', 7 | }; 8 | const event = {}; 9 | 10 | beforeAll(() => { 11 | helpers.setup(context); 12 | }); 13 | 14 | afterAll(() => { 15 | helpers.teardown(); 16 | }); 17 | 18 | test('returns a VoiceResponse', (done) => { 19 | const callback = (_err, result) => { 20 | expect(result).toBeInstanceOf(Twilio.twiml.VoiceResponse); 21 | done(); 22 | }; 23 | 24 | forwardCall(context, event, callback); 25 | }); 26 | 27 | test('forwards the call to the number from the context', (done) => { 28 | const callback = (_err, result) => { 29 | expect(result.toString()).toMatch( 30 | `${context.MY_PHONE_NUMBER}` 31 | ); 32 | done(); 33 | }; 34 | 35 | forwardCall(context, event, callback); 36 | }); 37 | -------------------------------------------------------------------------------- /forward-message-mailgun/.env: -------------------------------------------------------------------------------- 1 | # description: Your Mailgun API key 2 | # format: secret 3 | # link: https://app.mailgun.com/app/account/security/api_keys 4 | MAILGUN_API_KEY= 5 | 6 | # description: Your Mailgun verified domain you want to send emails from 7 | #format: text 8 | MG_VERIFIED_DOMAIN= 9 | 10 | # description: Messages sent to your Twilio number will be forwarded to this email address 11 | # format: email 12 | # required: true 13 | TO_EMAIL_ADDRESS= 14 | 15 | # description: The email address that Mailgun should send the email from 16 | # format: email 17 | # required: true 18 | FROM_EMAIL_ADDRESS= 19 | 20 | # description: The path to the webhook 21 | # configurable: false 22 | TWILIO_SMS_WEBHOOK_URL=/forward-message-mailgun 23 | 24 | -------------------------------------------------------------------------------- /forward-message-mailgun/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /forward-message-mailgun/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forward-message-mailgun", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "mailgun.js": "^3.3.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /forward-message/.env: -------------------------------------------------------------------------------- 1 | # description: A list of numbers in E.164 format you want to forward incoming messages to, separated by commas 2 | # format: list(phone_number) 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | FORWARDING_NUMBERS=+12223334444,+491761234567 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_SMS_WEBHOOK_URL=/forward-message -------------------------------------------------------------------------------- /forward-message/README.md: -------------------------------------------------------------------------------- 1 | # Forward Message 2 | 3 | This Function in `forward-message.js` returns the TwiML required to forward an incoming SMS message to a single number or each number in a comma-separated list set in the environment variables. 4 | 5 | ### Environment variables 6 | 7 | This Function expects one environment variable to be set. 8 | 9 | | Variable | Meaning | 10 | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | `FORWARDING_NUMBERS` | A list of numbers [in E.164 format](https://support.twilio.com/hc/en-us/articles/223183008-Formatting-International-Phone-Numbers) you want to forward incoming messages to, separated by commas | 12 | 13 | ### Parameters 14 | 15 | This Function expects the incoming request to be a messaging webhook. The parameters that will be used are `From` and `Body`. 16 | -------------------------------------------------------------------------------- /forward-message/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.1] 6 | ### Added 7 | - Updated and merged templates for single number and multi-number forwarding. 8 | - Added code comments. 9 | - Updated and added test cases. 10 | 11 | -------------------------------------------------------------------------------- /forward-message/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.1", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /frontline-quickstart/.env.example: -------------------------------------------------------------------------------- 1 | # description: Twilio phone number to send SMS from. Your patient will see this number. 2 | # format: phone_number 3 | # required: true 4 | # configurable: true 5 | TWILIO_PHONE_NUMBER= 6 | 7 | # description: This is the phone number of your example customer. You can send and receive text messages to this number. 8 | # format: phone_number 9 | # required: true 10 | EXAMPLE_CUSTOMER_1_PHONE_NUMBER= 11 | 12 | # description: (Optional) This is the phone number of your second example customer. You can send and receive text messages to this number. 13 | # format: phone_number 14 | # required: false 15 | EXAMPLE_CUSTOMER_2_PHONE_NUMBER= 16 | 17 | # description: This is the username of the user that you’ve added to your Identity Provider (IdP) that you will use to login to Frontline. 18 | # format: text 19 | # required: true 20 | SSO_USERNAME= -------------------------------------------------------------------------------- /frontline-quickstart/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | florantara 5 | -------------------------------------------------------------------------------- /frontline-quickstart/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /frontline-quickstart/assets/conversations-webhooks-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/frontline-quickstart/assets/conversations-webhooks-info.png -------------------------------------------------------------------------------- /frontline-quickstart/assets/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | ::selection { 8 | background: #f22f46; 9 | color: white; 10 | } 11 | 12 | a { 13 | color: #008cff; 14 | } 15 | #success-header { 16 | padding: 20px 0; 17 | display: flex; 18 | height: 180px; 19 | } 20 | #success-header h1 { 21 | margin-bottom: 1em; 22 | } 23 | main ul { 24 | margin-bottom: 3em; 25 | } 26 | main ul li { 27 | margin-bottom: 1.5em; 28 | } 29 | main ul li input[type='text'] { 30 | display: block; 31 | width: 100%; 32 | } 33 | 34 | div.content { 35 | max-width: 650px; 36 | padding: 0 20px; 37 | } 38 | section { 39 | margin-bottom: 20px; 40 | } 41 | 42 | p { 43 | margin-bottom: 1.5em; 44 | } 45 | 46 | p img { 47 | max-width: 100%; 48 | } 49 | -------------------------------------------------------------------------------- /frontline-quickstart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontline-quickstart", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@twilio-labs/runtime-helpers": "^0.1.2", 7 | "twilio": "^3.75.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /funlet-call-me/.env: -------------------------------------------------------------------------------- 1 | # description: Calls made to your Twilio number will get forwarded to this E.164-formatted phone number 2 | # format: phone_number 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | FUNLET_CALLME_PHONE_NUMBER=+12223334444 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/funlet-call-me 10 | -------------------------------------------------------------------------------- /funlet-call-me/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-call-me/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-call-me/tests/twimlet-whisper.log: -------------------------------------------------------------------------------- 1 | [WHISPER-2-1] Recorded Message 2 | 3 | 4 | 5 | https://example.com/recorded-message.mp3 6 | 7 | 8 | 9 | [WHISPER-2-2] Text Message 10 | 11 | 12 | 13 | Text message 14 | 15 | 16 | 17 | [WHISPER-2-3] Default Message 18 | 19 | 20 | 21 | You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept. 22 | 23 | 24 | 25 | [WHISPER-2-4] Human Check 26 | 27 | 28 | 29 | Text message 30 | 31 | 32 | 33 | 34 | [WHISPER-3-1] A Digit was Pressed 35 | 36 | 37 | 38 | [WHISPER-3-2] No Digits were Pressed 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /funlet-echo/.env: -------------------------------------------------------------------------------- 1 | # description: The default TwiML to echo back (can be overridden by the Twiml URL parameter) 2 | # format: text 3 | # link: https://www.twilio.com/docs/voice/twiml 4 | FUNLET_ECHO_TWIML=Hello, world! 5 | 6 | # description: The path to the webhook 7 | # configurable: false 8 | TWILIO_VOICE_WEBHOOK_URL=/funlet-echo 9 | -------------------------------------------------------------------------------- /funlet-echo/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-echo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-echo/tests/test-echo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Integration Tests for Echo Twimlet/Funlet 3 | # 4 | # Parameter: 5 | # $1 - URL of a deployed instance of the Echo Twimlet/Funlet 6 | # 7 | # Uses: 8 | # * curl - transfer a URL 9 | # * xmllint - command line XML tool 10 | # 11 | url="$1" 12 | 13 | indentXml(){ 14 | xmllint --format - 15 | } 16 | 17 | # Join HTTP parameters with '&' 18 | # and save them into $params. 19 | joinParams(){ 20 | regularIFS="$IFS" 21 | IFS=\& 22 | params="$*" 23 | IFS="$regularIFS" 24 | } 25 | 26 | # Function: query() 27 | # Query the Twimlet/Funlet using given HTTP parameters 28 | # 29 | # Parameters: 30 | # $* - list of key or key=value HTTP parameters, properly URL-encoded 31 | # 32 | query(){ 33 | joinParams "$@" 34 | curl -s "$url"?"$params" | indentXml 35 | } 36 | 37 | if test -z "$url" 38 | then 39 | echo "Usage: $0 url" >&2 40 | echo "for example: $0 'https://twimlets.com/echo'" 41 | exit 1 42 | fi 43 | 44 | echo '[Echo-1] Successful Echo' 45 | twiml='%3CResponse%3E%3CSay%3Eecho%20okay%3C%2FSay%3E%3C%2FResponse%3E' 46 | query Twiml="$twiml" 47 | -------------------------------------------------------------------------------- /funlet-echo/tests/twimlet-echo.log: -------------------------------------------------------------------------------- 1 | [Echo-1] Successful Echo 2 | 3 | 4 | echo okay 5 | 6 | -------------------------------------------------------------------------------- /funlet-find-me/.env: -------------------------------------------------------------------------------- 1 | # description: The phone numbers in this comma-separated list will be called in order. All phone numbers must follow the E.164 format 2 | # format: list(phone_number) 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | FUNLET_FINDME_PHONE_NUMBERS="+12345551234,+12345556789" 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/funlet-find-me 10 | -------------------------------------------------------------------------------- /funlet-find-me/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-find-me/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-find-me/tests/twimlet-whisper.log: -------------------------------------------------------------------------------- 1 | [WHISPER-2-1] Recorded Message 2 | 3 | 4 | 5 | https://example.com/recorded-message.mp3 6 | 7 | 8 | 9 | 10 | [WHISPER-2-2] Text Message 11 | 12 | 13 | 14 | Text message 15 | 16 | 17 | 18 | 19 | [WHISPER-2-3] Default Message 20 | 21 | 22 | 23 | You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept. 24 | 25 | 26 | 27 | 28 | [WHISPER-2-4] Human Check 29 | 30 | 31 | 32 | Text message 33 | 34 | 35 | 36 | 37 | [WHISPER-3-1] A Digit was Pressed 38 | 39 | 40 | 41 | [WHISPER-3-2] No Digits were Pressed 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /funlet-forward/.env: -------------------------------------------------------------------------------- 1 | # description: Calls made to your Twilio number will get forwarded to this E.164-formatted phone number 2 | # format: phone_number 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | FUNLET_FORWARD_PHONE_NUMBER=+12223334444 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/funlet-forward 10 | -------------------------------------------------------------------------------- /funlet-forward/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-forward/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-simple-menu/.env: -------------------------------------------------------------------------------- 1 | # description: The URL of a media file to , or a string to , to greet the caller. Can be overridden by the Message URL parameter. 2 | # format: text 3 | # required: true 4 | FUNLET_MENU_MESSAGE=Hello! Please select an option. 5 | 6 | # description: The path to the webhook 7 | # configurable: false 8 | TWILIO_VOICE_WEBHOOK_URL=/funlet-simple-menu 9 | -------------------------------------------------------------------------------- /funlet-simple-menu/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-simple-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-simple-menu/tests/twimlet-simple-menu.log: -------------------------------------------------------------------------------- 1 | [SIMPLE-MENU-1-1] Recorded Message 2 | 3 | 4 | 5 | https://example.com/recorded-message.mp3 6 | 7 | 8 | 9 | 10 | [SIMPLE-MENU-1-2] Text Message 11 | 12 | 13 | 14 | Text message 15 | 16 | 17 | 18 | 19 | [SIMPLE-MENU-1-3] Multiple Digits to Gather 20 | 21 | 22 | 23 | Text message 24 | 25 | 26 | 27 | 28 | [SIMPLE-MENU-2-1] Digits Pressed Match an Option 29 | Redirect: https://example.com/12345 30 | 31 | [SIMPLE-MENU-2-2] Digits Pressed Do Not Match Any Option 32 | 33 | 34 | I'm sorry, that wasn't a valid option. 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /funlet-simple-message/.env: -------------------------------------------------------------------------------- 1 | # description: The first URL to , or string to . 2 | # format: text 3 | # required: true 4 | FUNLET_MESSAGE_1=Hello, world! 5 | 6 | # description: The second URL to , or string to . 7 | # format: text 8 | FUNLET_MESSAGE_2= 9 | 10 | # description: The third URL to , or string to . 11 | # format: text 12 | FUNLET_MESSAGE_3= 13 | 14 | # description: The fourth URL to , or string to . 15 | # format: text 16 | FUNLET_MESSAGE_4= 17 | 18 | # description: The fifth URL to , or string to . 19 | # format: text 20 | FUNLET_MESSAGE_5= 21 | 22 | # description: The path to the webhook 23 | # configurable: false 24 | TWILIO_VOICE_WEBHOOK_URL=/funlet-simple-message 25 | -------------------------------------------------------------------------------- /funlet-simple-message/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-simple-message/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-simulring/.env: -------------------------------------------------------------------------------- 1 | # description: The phone numbers in this comma-separated list will be called simultaneously. All phone numbers must follow the E.164 format 2 | # format: list(phone_number) 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | FUNLET_SIMULRING_PHONE_NUMBERS="+12345551234,+12345556789" 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_VOICE_WEBHOOK_URL=/funlet-simulring 10 | -------------------------------------------------------------------------------- /funlet-simulring/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-simulring/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-simulring/tests/twimlet-whisper.log: -------------------------------------------------------------------------------- 1 | [WHISPER-2-1] Recorded Message 2 | 3 | 4 | 5 | https://example.com/recorded-message.mp3 6 | 7 | 8 | 9 | [WHISPER-2-2] Text Message 10 | 11 | 12 | 13 | Text message 14 | 15 | 16 | 17 | [WHISPER-2-3] Default Message 18 | 19 | 20 | 21 | You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept. 22 | 23 | 24 | 25 | [WHISPER-2-4] Human Check 26 | 27 | 28 | 29 | Text message 30 | 31 | 32 | 33 | 34 | [WHISPER-3-1] A Digit was Pressed 35 | 36 | 37 | 38 | [WHISPER-3-2] No Digits were Pressed 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /funlet-whisper/.env: -------------------------------------------------------------------------------- 1 | # description: If "true", check that the recipient is human by having them press a button. 2 | # format: text 3 | FUNLET_WHISPER_HUMAN_CHECK=false 4 | 5 | # description: The path to the webhook 6 | # configurable: false 7 | TWILIO_VOICE_WEBHOOK_URL=/funlet-whisper 8 | -------------------------------------------------------------------------------- /funlet-whisper/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /funlet-whisper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /funlet-whisper/tests/twimlet-whisper.log: -------------------------------------------------------------------------------- 1 | [WHISPER-1-1] Recorded Message 2 | 3 | 4 | 5 | https://example.com/recorded-message.mp3 6 | 7 | 8 | 9 | [WHISPER-1-2] Text Message 10 | 11 | 12 | 13 | Text message 14 | 15 | 16 | 17 | [WHISPER-1-3] Default Message 18 | 19 | 20 | 21 | You are receiving a call from +. 1. 9. 1. 6. 5. 5. 5. 0. 1. 2. 3. . Press any key to accept. 22 | 23 | 24 | 25 | [WHISPER-1-4] Human Check 26 | 27 | 28 | 29 | Text message 30 | 31 | 32 | 33 | 34 | [WHISPER-2-1] A Digit was Pressed 35 | 36 | 37 | 38 | [WHISPER-2-2] No Digits were Pressed 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /google-sheets/.env: -------------------------------------------------------------------------------- 1 | # description: A JSON key file for your Google Sheets service account 2 | # format: file(json) 3 | # contentKey: GOOGLE_CREDENTIALS_CONTENT 4 | # link: https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating_service_account_keys 5 | # required: true 6 | GOOGLE_CREDENTIALS=/auth.json 7 | 8 | # description: The document ID for your Google Sheets spreadsheet (from its URL: https://docs.google.com/spreadsheets/d/[id]/edit) 9 | # format: text 10 | # required: true 11 | DOCUMENT_ID= 12 | 13 | # description: The spreadsheet name to log to within your Google Sheets document 14 | # format: text 15 | # required: true 16 | SHEET_NAME=Sheet1 17 | 18 | # description: The path to the webhook 19 | # configurable: false 20 | TWILIO_SMS_WEBHOOK_URL=/log-sms 21 | -------------------------------------------------------------------------------- /google-sheets/assets/auth.private.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "", 4 | "private_key_id": "", 5 | "private_key": "", 6 | "client_email": "", 7 | "client_id": "", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "" 12 | } 13 | -------------------------------------------------------------------------------- /google-sheets/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /google-sheets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-sheets", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "googleapis": "^65.0.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /google-sheets/tests/auth.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "test project ID", 4 | "private_key_id": "test private key ID", 5 | "private_key": "test private key", 6 | "client_email": "test@example.org", 7 | "client_id": "test client ID", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://example.org/test" 12 | } 13 | -------------------------------------------------------------------------------- /hello-messaging/.env: -------------------------------------------------------------------------------- 1 | # description: The path to the webhook 2 | # configurable: false 3 | TWILIO_SMS_WEBHOOK_URL=/hello-messaging -------------------------------------------------------------------------------- /hello-messaging/README.md: -------------------------------------------------------------------------------- 1 | # Hello Messaging 2 | 3 | This is a basic Twilio [Messaging TwiML](https://www.twilio.com/docs/sms/twiml) template Function that will return TwiML to say "Hello World" 4 | 5 | ## Environment variables 6 | 7 | This Function requires no environment variables to run successfully. 8 | 9 | ## Parameters 10 | 11 | This Function requires no URL or POST parameters to run successfully. 12 | -------------------------------------------------------------------------------- /hello-messaging/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /hello-messaging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /hello-messaging/tests/hello-messaging.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const helloVoice = require('../functions/hello-messaging.protected').handler; 3 | const Twilio = require('twilio'); 4 | 5 | const context = {}; 6 | const event = {}; 7 | 8 | beforeAll(() => { 9 | helpers.setup(context); 10 | }); 11 | 12 | afterAll(() => { 13 | helpers.teardown(); 14 | }); 15 | 16 | test('returns a VoiceResponse', (done) => { 17 | const callback = (_err, result) => { 18 | expect(result).toBeInstanceOf(Twilio.twiml.MessagingResponse); 19 | done(); 20 | }; 21 | 22 | helloVoice(context, event, callback); 23 | }); 24 | 25 | test('says Hello World', (done) => { 26 | const callback = (_err, result) => { 27 | expect(result.toString()).toMatch('Hello World'); 28 | done(); 29 | }; 30 | 31 | helloVoice(context, event, callback); 32 | }); 33 | -------------------------------------------------------------------------------- /hello-voice/.env: -------------------------------------------------------------------------------- 1 | # description: The path to the webhook 2 | # configurable: false 3 | TWILIO_VOICE_WEBHOOK_URL=/hello-voice -------------------------------------------------------------------------------- /hello-voice/README.md: -------------------------------------------------------------------------------- 1 | # Hello Voice 2 | 3 | This is a basic Twilio [Voice TwiML](https://www.twilio.com/docs/voice/twiml) template Function that will return TwiML to say "Hello World" 4 | 5 | ## Environment variables 6 | 7 | This Function requires no environment variables to run successfully. 8 | 9 | ## Parameters 10 | 11 | This Function requires no URL or POST parameters to run successfully. 12 | -------------------------------------------------------------------------------- /hello-voice/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /hello-voice/functions/hello-voice.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const twiml = new Twilio.twiml.VoiceResponse(); 3 | twiml.say('Hello World'); 4 | callback(null, twiml); 5 | }; 6 | -------------------------------------------------------------------------------- /hello-voice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /hello-voice/tests/hello-voice.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const helloVoice = require('../functions/hello-voice.protected').handler; 3 | const Twilio = require('twilio'); 4 | 5 | const context = {}; 6 | const event = {}; 7 | 8 | beforeAll(() => { 9 | helpers.setup(context); 10 | }); 11 | 12 | afterAll(() => { 13 | helpers.teardown(); 14 | }); 15 | 16 | test('returns a VoiceResponse', (done) => { 17 | const callback = (_err, result) => { 18 | expect(result).toBeInstanceOf(Twilio.twiml.VoiceResponse); 19 | done(); 20 | }; 21 | 22 | helloVoice(context, event, callback); 23 | }); 24 | 25 | test('says Hello World', (done) => { 26 | const callback = (_err, result) => { 27 | expect(result.toString()).toMatch('Hello World'); 28 | done(); 29 | }; 30 | 31 | helloVoice(context, event, callback); 32 | }); 33 | -------------------------------------------------------------------------------- /hello-world/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/hello-world/.env -------------------------------------------------------------------------------- /hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Hello world! 2 | 3 | This Function will return the string 'Hello world'. 4 | 5 | ## Environment variables 6 | 7 | This Function requires no environment variables to run successfully. 8 | 9 | ## Parameters 10 | 11 | This Function requires no URL or POST parameters to run successfully. 12 | -------------------------------------------------------------------------------- /hello-world/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /hello-world/cypress/integration/hello-world.spec.js: -------------------------------------------------------------------------------- 1 | describe('GET /hello-world', () => { 2 | it('returns valid hello world', () => { 3 | cy.request({ 4 | method: 'GET', 5 | url: '/hello-world', 6 | headers: {}, 7 | failOnStatusCode: false, 8 | }).then((response) => { 9 | expect(response.body).to.eq('Hello world!'); 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /hello-world/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | }; 23 | -------------------------------------------------------------------------------- /hello-world/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /hello-world/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /hello-world/e2e.js: -------------------------------------------------------------------------------- 1 | const { runE2eTestSuite } = require('../_helpers/test-suite'); 2 | 3 | runE2eTestSuite({ 4 | env: { 5 | // put any environment variables for Twilio Functions here 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /hello-world/functions/hello-world.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | callback(null, 'Hello world!'); 3 | }; 4 | -------------------------------------------------------------------------------- /hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {}, 5 | "scripts": { 6 | "e2e": "node e2e.js" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hello-world/tests/hello-world.test.js: -------------------------------------------------------------------------------- 1 | const helloWorld = require('../functions/hello-world').handler; 2 | 3 | test('returns the string "Hello world!"', (done) => { 4 | const callback = (_err, result) => { 5 | expect(result).toBe('Hello world!'); 6 | done(); 7 | }; 8 | helloWorld({}, {}, callback); 9 | }); 10 | -------------------------------------------------------------------------------- /http-redirect/.env: -------------------------------------------------------------------------------- 1 | # description: The URL you want your redirect Function to redirect to 2 | # format: url 3 | # required: true 4 | HTTP_REDIRECT_URL= -------------------------------------------------------------------------------- /http-redirect/README.md: -------------------------------------------------------------------------------- 1 | # HTTP Redirect 2 | 3 | This Function redirects a request from Twilio Functions to another URL by setting the Location header to the respective URL. 4 | 5 | ## Environment variables 6 | 7 | This Function expects the following environment variables set: 8 | 9 | | Variable | Meaning | Required | 10 | | :------------------ | :-------------------------------- | :------- | 11 | | `HTTP_REDIRECT_URL` | A URL to redirect the requests to | Yes | 12 | 13 | ## Parameters 14 | 15 | This Function doesn't expect any parameters passed. 16 | -------------------------------------------------------------------------------- /http-redirect/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /http-redirect/functions/redirect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Redirect Function 3 | * 4 | * This Function redirects a request from Twilio Functions to another URL by 5 | * setting the Location header to the respective URL 6 | */ 7 | exports.handler = function (context, event, callback) { 8 | const response = new Twilio.Response(); 9 | response.appendHeader('Location', context.HTTP_REDIRECT_URL); 10 | callback(null, response); 11 | }; 12 | -------------------------------------------------------------------------------- /http-redirect/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /http-redirect/tests/redirect.test.js: -------------------------------------------------------------------------------- 1 | const tokenFunction = require('../functions/redirect').handler; 2 | const helpers = require('../../test/test-helper'); 3 | 4 | const baseContext = { 5 | HTTP_REDIRECT_URL: 'https://twil.io', 6 | }; 7 | 8 | describe('video-token/token', () => { 9 | beforeAll(() => { 10 | helpers.setup({}); 11 | }); 12 | afterAll(() => { 13 | helpers.teardown(); 14 | }); 15 | 16 | test('returns a valid token with default room', (done) => { 17 | const callback = (_err, result) => { 18 | expect(result).toBeDefined(); 19 | expect(result._headers).toEqual({ 20 | Location: 'https://twil.io', 21 | }); 22 | 23 | done(); 24 | }; 25 | tokenFunction(baseContext, {}, callback); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /hunt/.env: -------------------------------------------------------------------------------- 1 | # description: A comma separated list of numbers in E.164 format that you want to dial in order 2 | # format: phone_number 3 | # required: true 4 | PHONE_NUMBERS=+12223334444,+491761234567 5 | 6 | # description: A URL to redirect the call to if none of the numbers answer. If this is not supplied then the call will just hang up once it has exhausted all the options 7 | # format: url 8 | # required: false 9 | FINAL_URL= 10 | 11 | # description: The path to the webhook 12 | # configurable: false 13 | TWILIO_VOICE_WEBHOOK_URL=/hunt -------------------------------------------------------------------------------- /hunt/README.md: -------------------------------------------------------------------------------- 1 | # Hunt / Find Me 2 | 3 | This Function takes an array of numbers and will return the TwiML required to dial each number in order until one answers. This is an initial implementation of the ["Find Me" Twimlet](https://www.twilio.com/labs/twimlets/findme). 4 | 5 | ## Environment variables 6 | 7 | This Function expects one environment variable to be set. 8 | 9 | | Variable | Meaning | Required | 10 | | :---------------- | :------ | :------- | 11 | | `PHONE_NUMBERS` | A comma separated list of numbers [in E.164 format](https://support.twilio.com/hc/en-us/articles/223183008-Formatting-International-Phone-Numbers) that you want to dial in order | Yes | 12 | | `FINAL_URL` | A URL to redirect the call to if none of the numbers answer. If this is not supplied then the call will just hang up once it has exhausted all the options | No | 13 | 14 | ## Parameters 15 | 16 | This Function expects the incoming request to be a voice webhook. The parameters that will be used are `DialCallStatus` and a custom parameter, `nextNumber` that the function itself provides. 17 | -------------------------------------------------------------------------------- /hunt/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /hunt/functions/hunt.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const numbers = context.PHONE_NUMBERS.split(',').map((number) => 3 | number.trim() 4 | ); 5 | const response = new Twilio.twiml.VoiceResponse(); 6 | if (event.DialCallStatus === 'complete') { 7 | // Call was answered and completed 8 | response.hangup(); 9 | } else if (event.finished === 'true') { 10 | if (context.FINAL_URL) { 11 | response.redirect(context.FINAL_URL); 12 | } else { 13 | response.hangup(); 14 | } 15 | } else { 16 | const numberToDial = event.nextNumber ? event.nextNumber : numbers[0]; 17 | const currentNumberIndex = numbers.indexOf(numberToDial); 18 | let url; 19 | if (currentNumberIndex + 1 === numbers.length) { 20 | // No more numbers to call after this. 21 | url = '/hunt?finished=true'; 22 | } else { 23 | const nextNumber = numbers[currentNumberIndex + 1]; 24 | url = `/hunt?nextNumber=${encodeURIComponent(nextNumber)}`; 25 | } 26 | const dial = response.dial({ action: url }); 27 | dial.number(numberToDial); 28 | } 29 | callback(null, response); 30 | }; 31 | -------------------------------------------------------------------------------- /hunt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /international-telephone-input/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/international-telephone-input/.env -------------------------------------------------------------------------------- /international-telephone-input/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /international-telephone-input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^5.2.2" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /json-webhook/.env: -------------------------------------------------------------------------------- 1 | # description: The URL for the webhook this function should call 2 | # format: text 3 | # required: true 4 | WEBHOOK_URL= 5 | 6 | # description: The path to the Twilio Function webhook 7 | # configurable: false 8 | TWILIO_SMS_WEBHOOK_URL=/event-to-webhook 9 | -------------------------------------------------------------------------------- /json-webhook/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /json-webhook/functions/event-to-webhook.protected.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | 3 | exports.handler = async function (context, event, callback) { 4 | const twiml = new Twilio.twiml.MessagingResponse(); 5 | 6 | try { 7 | // IFTTT only pulls the fields value1, value2, and value3 from webhook JSON; 8 | event.value1 = event.MessageSid; 9 | event.value2 = event.From; 10 | event.value3 = event.Body; 11 | 12 | const res = await fetch(context.WEBHOOK_URL, { 13 | method: 'POST', 14 | headers: { 15 | 'Content-Type': 'application/json', 16 | }, 17 | body: JSON.stringify(event), 18 | }); 19 | 20 | if (res.ok) { 21 | twiml.message('The SMS was successfully forwarded to your webhook.'); 22 | return callback(null, twiml); 23 | } 24 | 25 | return callback(res.statusText); 26 | } catch (error) { 27 | return callback(error); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /json-webhook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-webhook", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "node-fetch": "^2.6.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /list-numbers/.env: -------------------------------------------------------------------------------- 1 | # Variables for function "" 2 | # --- 3 | # description: Set the password that will allow you to use the application 4 | Password=SuperSecretPassword 5 | -------------------------------------------------------------------------------- /list-numbers/.owners: -------------------------------------------------------------------------------- 1 | dsTw5 (dsumberac) 2 | # Insert your Github username here 3 | -------------------------------------------------------------------------------- /list-numbers/Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.1.0] 6 | ### Added 7 | - Added the feature to extract the list of numbers from all subaccounts and Main account at the same time 8 | - Added the option to select the downloaded file format 9 | 10 | -------------------------------------------------------------------------------- /list-numbers/e2e.js: -------------------------------------------------------------------------------- 1 | const { runE2eTestSuite } = require('../_helpers/test-suite'); 2 | 3 | runE2eTestSuite({ 4 | env: { 5 | Password: 1, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /list-numbers/functions/get_account.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | let finalData = null; 3 | const accountSid = { acc: process.env.ACCOUNT_SID }; 4 | const authHeader = event.request.headers.authorization; 5 | 6 | if (authHeader !== process.env.Password) { 7 | finalData = { er: 0 }; 8 | return callback(null, finalData); 9 | } 10 | 11 | try { 12 | return callback(null, accountSid); 13 | } catch (error) { 14 | console.error(error.message); 15 | response.setStatusCode(error.status || 400); 16 | response.setBody({ error: error.message }); 17 | return callback(null, response); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /list-numbers/functions/mask_account.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const masked = 3 | String(process.env.ACCOUNT_SID).slice(0, 28).replace(/./g, '*') + 4 | String(process.env.ACCOUNT_SID).slice(-6); 5 | const accountSid = { acc: masked }; 6 | 7 | try { 8 | return callback(null, accountSid); 9 | } catch (error) { 10 | console.error(error.message); 11 | response.setStatusCode(error.status || 400); 12 | response.setBody({ error: error.message }); 13 | return callback(null, response); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /list-numbers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /list-numbers/tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/list-numbers/tests/.gitkeep -------------------------------------------------------------------------------- /lookup/.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/lookup/.env -------------------------------------------------------------------------------- /lookup/assets/styles.css: -------------------------------------------------------------------------------- 1 | pre { 2 | white-space: pre-wrap; 3 | } 4 | 5 | .alert { 6 | padding: 15px; 7 | margin-top: 10px; 8 | border: 1px solid transparent; 9 | border-radius: 4px; 10 | } 11 | 12 | .info { 13 | border-color: #bce8f1; 14 | color: #31708f; 15 | background-color: #d9edf7; 16 | } 17 | 18 | .error { 19 | color: #a94442; 20 | background-color: #f2dede; 21 | border-color: #ebccd1; 22 | } 23 | -------------------------------------------------------------------------------- /lookup/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.1.0] 4 | ### Changed 5 | - Moved all requests to Lookup V2 6 | 7 | ## [1.0.1] 8 | ### Added 9 | - Adds option for V2 Line Type Intelligence data package 10 | 11 | ## [1.0.0] 12 | ### Added 13 | - Initial release. 14 | 15 | -------------------------------------------------------------------------------- /lookup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.1", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^5.2.2" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /magic-links/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= 6 | 7 | # description: URL path linked in the email template to check verifications 8 | # format: text 9 | # required: true 10 | CALLBACK_PATH=verify.html -------------------------------------------------------------------------------- /magic-links/assets/email-template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 |
16 |

17 | More details about how to set up email verification in the documentation. 18 |

19 |

20 | Click here to verify email 23 |

24 |
25 | 26 | -------------------------------------------------------------------------------- /magic-links/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /magic-links/magic-link-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/magic-links/magic-link-demo.gif -------------------------------------------------------------------------------- /magic-links/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /masked-number/.env: -------------------------------------------------------------------------------- 1 | # description: Messages sent to your Twilio number will get forwarded to this E.164-formatted phone number 2 | # format: phone_number 3 | # required: true 4 | # link: https://www.twilio.com/docs/glossary/what-e164 5 | MY_PHONE_NUMBER=+12223334444 6 | 7 | # description: The path to the webhook 8 | # configurable: false 9 | TWILIO_SMS_WEBHOOK_URL=/relay-sms 10 | -------------------------------------------------------------------------------- /masked-number/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /masked-number/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "masked-number", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /mcp-server/.env.example: -------------------------------------------------------------------------------- 1 | # description: Your Twilio Account SID 2 | # format: text 3 | # link: https://www.twilio.com/console 4 | # required: true 5 | ACCOUNT_SID=ACxxx 6 | 7 | # description: Your Twilio Auth Token 8 | # format: text 9 | # link: https://www.twilio.com/console 10 | # required: true 11 | AUTH_TOKEN=abc 12 | 13 | # description: Your Twilio API Key 14 | # format: text 15 | # link: https://www.twilio.com/console/project/api-keys 16 | # required: true 17 | API_KEY=SKxxx 18 | 19 | # description: Your Twilio API Secret 20 | # format: text 21 | # link: https://www.twilio.com/console/project/api-keys 22 | # required: true 23 | API_SECRET=abc 24 | -------------------------------------------------------------------------------- /mcp-server/.owners: -------------------------------------------------------------------------------- 1 | ktalebian 2 | vingiarrusso 3 | bpartridge 4 | # Insert your Github username here 5 | -------------------------------------------------------------------------------- /mcp-server/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /mcp-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcp-server", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@modelcontextprotocol/sdk": "^1.11.2", 7 | "@twilio-alpha/mcp": "^0.5.1", 8 | "@twilio/runtime-handler": "2.0.1", 9 | "crypto": "^1.0.1", 10 | "dotenv": "^16.5.0", 11 | "twilio": "^5.6", 12 | "zod": "^3.24.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /mcp-server/tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/mcp-server/tests/.gitkeep -------------------------------------------------------------------------------- /never-gonna-give-you-up/.env: -------------------------------------------------------------------------------- 1 | # description: The path to the webhook 2 | # configurable: false 3 | TWILIO_VOICE_WEBHOOK_URL=/never-gonna-give-you-up -------------------------------------------------------------------------------- /never-gonna-give-you-up/README.md: -------------------------------------------------------------------------------- 1 | # Never gonna give you up 2 | 3 | This Function will never let you down to play Rick Astley's hit song! 4 | 5 | You can use this template to play your own song as well. 6 | 7 | ## Environment variables 8 | 9 | This Function requires no environment variables to run successfully. 10 | 11 | ## Parameters 12 | 13 | This Function requires no URL or POST parameters to run successfully. 14 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/functions/never-gonna-give-you-up.protected.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const twiml = new Twilio.twiml.VoiceResponse(); 3 | twiml.play('https://demo.twilio.com/docs/classic.mp3'); 4 | 5 | callback(null, twiml); 6 | }; 7 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/never-gonna-give-you-up.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const twiml = new Twilio.twiml.VoiceResponse(); 3 | twiml.play('https://demo.twilio.com/docs/classic.mp3'); 4 | 5 | callback(null, twiml); 6 | }; 7 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/never-gonna-give-you-up.test.js: -------------------------------------------------------------------------------- 1 | const neverGonnaGiveYouUp = require('./never-gonna-give-you-up').handler; 2 | const helpers = require('../test/test-helper'); 3 | 4 | describe('', () => { 5 | beforeAll(() => { 6 | helpers.setup({}); 7 | }); 8 | afterAll(() => { 9 | helpers.teardown(); 10 | }); 11 | 12 | test('returns the right song', (done) => { 13 | const callback = (_err, result) => { 14 | expect(result).toBeInstanceOf(Twilio.twiml.VoiceResponse); 15 | expect(result.toString()).toMatch( 16 | 'https://demo.twilio.com/docs/classic.mp3' 17 | ); 18 | done(); 19 | }; 20 | neverGonnaGiveYouUp({}, {}, callback); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /never-gonna-give-you-up/tests/never-gonna-give-you-up.test.js: -------------------------------------------------------------------------------- 1 | const neverGonnaGiveYouUp = 2 | require('../functions/never-gonna-give-you-up.protected').handler; 3 | const helpers = require('../../test/test-helper'); 4 | 5 | describe('', () => { 6 | beforeAll(() => { 7 | helpers.setup({}); 8 | }); 9 | afterAll(() => { 10 | helpers.teardown(); 11 | }); 12 | 13 | test('returns the right song', (done) => { 14 | const callback = (_err, result) => { 15 | expect(result).toBeInstanceOf(Twilio.twiml.VoiceResponse); 16 | expect(result.toString()).toMatch( 17 | 'https://demo.twilio.com/docs/classic.mp3' 18 | ); 19 | done(); 20 | }; 21 | neverGonnaGiveYouUp({}, {}, callback); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /passkeys-backend/.env.example: -------------------------------------------------------------------------------- 1 | # description: The URL of the API for passkeys 2 | # format: url 3 | # required: true 4 | API_URL=https://comms.twilio.com/preview 5 | 6 | # description: [Optional] Comma separated domains for Android application 7 | # format: list(text) 8 | # required: false 9 | ANDROID_APP_KEYS= 10 | -------------------------------------------------------------------------------- /passkeys-backend/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | nicolas-camacho 5 | -------------------------------------------------------------------------------- /passkeys-backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /passkeys-backend/assets/.well-know/apple-app-site-association: -------------------------------------------------------------------------------- 1 | { 2 | "webcredentials": { 3 | "apps": [ 4 | "{ORIGIN_IOS_APP_HASH}" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /passkeys-backend/assets/.well-know/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": [ 4 | "delegate_permission/common.handle_all_urls", 5 | "delegate_permission/common.get_login_creds" 6 | ], 7 | "target": { 8 | "namespace": "web", 9 | "site": "{RELYING_PARTY}" 10 | } 11 | }, 12 | { 13 | "relation": [ 14 | "delegate_permission/common.handle_all_urls", 15 | "delegate_permission/common.get_login_creds" 16 | ], 17 | "target": { 18 | "namespace": "android_app", 19 | "package_name": "com.twilio.passkeys.android", 20 | "sha256_cert_fingerprints": ["{FINGERPRINT_CERTIFICATION_HASH}"] 21 | } 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /passkeys-backend/assets/services/helpers.private.js: -------------------------------------------------------------------------------- 1 | const detectMissingParams = (paramNames, event) => { 2 | const missingParams = paramNames.filter( 3 | (param) => !event.hasOwnProperty(param) 4 | ); 5 | return missingParams.length > 0 ? missingParams : null; 6 | }; 7 | 8 | const isEmpty = (requestBody) => { 9 | return Object.keys(requestBody).length === 0; 10 | }; 11 | 12 | module.exports = { 13 | detectMissingParams, 14 | isEmpty, 15 | }; 16 | -------------------------------------------------------------------------------- /passkeys-backend/functions/authentication/start.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | // eslint-disable-next-line consistent-return 4 | exports.handler = async (context, _, callback) => { 5 | const { DOMAIN_NAME, API_URL } = context; 6 | 7 | const response = new Twilio.Response(); 8 | response.appendHeader('Content-Type', 'application/json'); 9 | 10 | const { username, password } = context.getTwilioClient(); 11 | 12 | const requestBody = { 13 | content: { 14 | // eslint-disable-next-line camelcase 15 | rp_id: DOMAIN_NAME, 16 | }, 17 | }; 18 | 19 | const challengeURL = `${API_URL}/Verifications`; 20 | 21 | try { 22 | const APIResponse = await axios.post(challengeURL, requestBody, { 23 | auth: { 24 | username, 25 | password, 26 | }, 27 | }); 28 | 29 | response.setStatusCode(200); 30 | response.setBody(APIResponse.data.next_step); 31 | } catch (error) { 32 | const statusCode = error.status || 400; 33 | response.setStatusCode(statusCode); 34 | response.setBody(error.message); 35 | } 36 | 37 | return callback(null, response); 38 | }; 39 | -------------------------------------------------------------------------------- /passkeys-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passkeys-backend", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "twilio": "^5.3.3", 7 | "axios": "^1.7.7" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /patient-appointment-management/assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/patient-appointment-management/assets/architecture.png -------------------------------------------------------------------------------- /patient-appointment-management/assets/state-transition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/patient-appointment-management/assets/state-transition.png -------------------------------------------------------------------------------- /patient-appointment-management/assets/token-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/patient-appointment-management/assets/token-flow.png -------------------------------------------------------------------------------- /patient-appointment-management/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /patient-appointment-management/functions/refresh-token.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-destructuring, dot-notation, consistent-return, spaced-comment */ 2 | exports.handler = function (context, event, callback) { 3 | const path = Runtime.getFunctions()['auth'].path; 4 | const { createAppToken, isValidAppToken } = require(path); 5 | 6 | const ac = context.ACCOUNT_SID; 7 | 8 | //assert(event.token, 'missing event.token'); 9 | if (!isValidAppToken(event.token, context)) { 10 | const response = new Twilio.Response(); 11 | response.setStatusCode(401); 12 | response.appendHeader( 13 | 'Error-Message', 14 | 'Invalid or expired token. Please refresh the page and login again.' 15 | ); 16 | response.appendHeader('Content-Type', 'application/json'); 17 | response.setBody({ message: 'Unauthorized' }); 18 | 19 | return callback(null, response); 20 | } 21 | 22 | const response = new Twilio.Response(); 23 | response.appendHeader('Content-Type', 'application/json'); 24 | response.setBody({ 25 | token: createAppToken('refresh', context), 26 | }); 27 | callback(null, response); 28 | }; 29 | -------------------------------------------------------------------------------- /patient-appointment-management/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "patient-appointment-management", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "aws-sdk": "^2.925.0", 7 | "jsonwebtoken": "^8.5.1", 8 | "jszip": "^3.6.0", 9 | "twilio": "^3.61.0", 10 | "uuid": "^8.3.2", 11 | "validator": "^13.6.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /reminder-message/.env.example: -------------------------------------------------------------------------------- 1 | # description: SID of a Twilio Messaging Service that will be used for your reminder email. 2 | # format: sid 3 | # link: https://console.twilio.com/us1/develop/sms/services 4 | # required: true 5 | MESSAGING_SERVICE_SID= 6 | 7 | # description: Delay of how many minutes it should take after the incoming message to sent out a reminder. Minimum: 15 minutes 8 | # format: number 9 | # required: false 10 | DELAY_IN_MINUTES= 11 | 12 | # description: The path to the webhook 13 | # configurable: false 14 | TWILIO_SMS_WEBHOOK_URL=/respond -------------------------------------------------------------------------------- /reminder-message/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /reminder-message/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reminder-message", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@twilio-labs/runtime-helpers": "^0.1.2", 7 | "twilio": "^3.83.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /segment-event-notification/.env: -------------------------------------------------------------------------------- 1 | # description: The name of the Segment event you want to be notified about 2 | # format: text 3 | # link: https://segment.com/docs/connections/spec/track/#event 4 | # required: true 5 | SEGMENT_EVENT= 6 | 7 | # description: The Twilio phone number to send SMS notifications from 8 | # format: phone_number 9 | # required: true 10 | TWILIO_PHONE_NUMBER=+12223334444 11 | 12 | 13 | # description: Segment event notifications will be sent to this E.164-formatted phone number 14 | # format: phone_number 15 | # required: true 16 | # link: https://www.twilio.com/docs/glossary/what-e164 17 | MY_PHONE_NUMBER=+12223334444 18 | -------------------------------------------------------------------------------- /segment-event-notification/assets/segment-event-tester.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/segment-event-notification/assets/segment-event-tester.png -------------------------------------------------------------------------------- /segment-event-notification/assets/segment-webhook-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/segment-event-notification/assets/segment-webhook-settings.png -------------------------------------------------------------------------------- /segment-event-notification/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /segment-event-notification/functions/track-sms.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const client = context.getTwilioClient(); 3 | 4 | try { 5 | if (event.type === 'track' && event.event === context.SEGMENT_EVENT) { 6 | let body = `The Segment event "${event.event}" occurred.`; 7 | 8 | if (event.properties && Object.keys(event.properties).length > 0) { 9 | body += '\n\n'; 10 | 11 | for (const [key, value] of Object.entries(event.properties)) { 12 | body += `${key}: ${JSON.stringify(value)}\n`; 13 | } 14 | } 15 | 16 | await client.messages.create({ 17 | to: context.MY_PHONE_NUMBER, 18 | from: context.TWILIO_PHONE_NUMBER, 19 | body, 20 | }); 21 | } 22 | } catch (err) { 23 | return callback(err); 24 | } 25 | 26 | return callback(null, {}); 27 | }; 28 | -------------------------------------------------------------------------------- /segment-event-notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "segment-event-notification", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /sip-quickstart/.env: -------------------------------------------------------------------------------- 1 | # description: Choose a name for your application 2 | # format: text 3 | # required: true 4 | APP_NAME=SIP Quickstart 5 | 6 | # description: Set a password for your app. Users who want to use the admin interface will have to use this password to access it 7 | # format: text 8 | # required: true 9 | ADMIN_PASSWORD=default 10 | 11 | # description: The default password for your SIP user 12 | # format: text 13 | # required: true 14 | DEFAULT_SIP_USER_PASSWORD=ThisIs1Password! 15 | -------------------------------------------------------------------------------- /sip-quickstart/assets/extensions.private.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | name: 'Alice Allison', 4 | username: 'alice', 5 | extension: '100', 6 | }, 7 | { 8 | name: 'Bob Bobberson', 9 | username: 'bob', 10 | extension: '200', 11 | }, 12 | { 13 | name: 'Charlie Charleston', 14 | username: 'charlie', 15 | extension: '300', 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /sip-quickstart/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /sip-quickstart/functions/admin/check-status.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const status = require(assets['/admin/statuses.js'].path); 3 | const { checkAuthorization } = require(assets['/admin/shared.js'].path); 4 | const environmentFunction = status.environment; 5 | const statusFunctions = status.statuses; 6 | 7 | async function getStatuses(context) { 8 | // This should be `Promise.allSettled` with a filter, but the node version is off 9 | // eslint-disable-next-line consistent-return 10 | const promises = statusFunctions.map(async (fn) => { 11 | try { 12 | return await fn(context); 13 | } catch (err) { 14 | console.error(`Status check failed for ${fn.name}: ${err}`); 15 | } 16 | }); 17 | const results = await Promise.all(promises); 18 | return results.filter((result) => result !== undefined); 19 | } 20 | 21 | exports.handler = async function (context, event, callback) { 22 | if (!checkAuthorization(context, event, callback)) { 23 | return; 24 | } 25 | const environment = await environmentFunction(context); 26 | const statuses = await getStatuses(context); 27 | callback(null, { 28 | environment, 29 | statuses, 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /sip-quickstart/functions/admin/login.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const { checkAuthorization, createToken } = require( 3 | assets['/admin/shared.js'].path 4 | ); 5 | 6 | // eslint-disable-next-line consistent-return 7 | exports.handler = function (context, event, callback) { 8 | // Create a token from the password, and use it to check by setting it 9 | // eslint-disable-next-line no-multi-assign 10 | const token = (event.token = createToken(context, event.password)); 11 | // Short-circuits 12 | if (checkAuthorization(context, event, callback)) { 13 | return callback(null, { token }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /sip-quickstart/functions/outbound-calls.js: -------------------------------------------------------------------------------- 1 | exports.handler = function (context, event, callback) { 2 | const twiml = new Twilio.twiml.VoiceResponse(); 3 | const sipTo = event.To; 4 | // eslint-disable-next-line prefer-named-capture-group 5 | const matches = sipTo.match(/sip:([+]?[0-9]+)@/); 6 | if (matches && matches.length > 1) { 7 | const to = matches[1]; 8 | console.log( 9 | `Dialing ${to} from ${sipTo} with Caller ID ${context.CALLER_ID}` 10 | ); 11 | twiml.dial(to, { callerId: context.CALLER_ID }); 12 | } else { 13 | console.log(`Dialing ${sipTo}`); 14 | twiml.dial().sip(sipTo); 15 | } 16 | callback(null, twiml); 17 | }; 18 | -------------------------------------------------------------------------------- /sip-quickstart/functions/sip-configuration.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const extensions = require(assets['/extensions.js'].path); 3 | 4 | exports.handler = async (context, event, callback) => { 5 | try { 6 | const client = context.getTwilioClient(); 7 | const sipDomain = await client.sip.domains(context.SIP_DOMAIN_SID).fetch(); 8 | const localizedSipDomainName = sipDomain.domainName.replace( 9 | '.sip.twilio.com', 10 | '.sip.us1.twilio.com' 11 | ); 12 | return callback(null, { 13 | initialized: context.INITIALIZED, 14 | appName: context.APP_NAME, 15 | incomingNumber: context.INCOMING_NUMBER, 16 | callerId: context.CALLER_ID, 17 | sipDomainName: sipDomain.domainName, 18 | extensions, 19 | localizedSipDomainName, 20 | }); 21 | } catch (err) { 22 | return callback(err); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /sip-quickstart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sip-quickstart", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "common-tags": "^1.8.0", 7 | "twilio": "^3.61.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sms-broadcast/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Notify Service (starts with IS) 2 | # format: sid 3 | # link: https://www.twilio.com/console/notify/services 4 | # required: true 5 | BROADCAST_NOTIFY_SERVICE_SID= 6 | 7 | # description: A comma separated list of numbers in E.164 format that are allowed to trigger a broadcast 8 | # format: phone_number 9 | # required: true 10 | BROADCAST_ADMIN_NUMBERS=+12223334444,+491761234567 11 | 12 | # description: The path to the webhook 13 | # configurable: false 14 | TWILIO_SMS_WEBHOOK_URL=/broadcast 15 | -------------------------------------------------------------------------------- /sms-broadcast/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /sms-broadcast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sms-broadcast", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /sms-notifications/.env: -------------------------------------------------------------------------------- 1 | # description: The Twilio phone number to send broadcast SMS from 2 | # format: phone_number 3 | # required: true 4 | TWILIO_PHONE_NUMBER= 5 | 6 | # description: Choose a passcode for your app. Users have to use this passcode to send SMS broadcasts 7 | # format: text 8 | # required: true 9 | PASSCODE= 10 | -------------------------------------------------------------------------------- /sms-notifications/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /sms-notifications/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sms-notifications", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /stripe-payment-link-sms/.env: -------------------------------------------------------------------------------- 1 | # description: A Stripe secret key 2 | # format: secret 3 | # link: https://dashboard.stripe.com/test/apikeys 4 | # required: true 5 | STRIPE_SECRET_KEY= 6 | 7 | 8 | # description: Sender phone number. Default is "STRIPEDEMO" 9 | # format: phone_number 10 | # link: https://www.twilio.com/console/phone-numbers/getting-started 11 | # required: false 12 | TWILIO_PHONE_NUMBER= 13 | 14 | 15 | # description: The path to the webhook 16 | # configurable: false 17 | TWILIO_SMS_WEBHOOK_URL=/create-invoice 18 | -------------------------------------------------------------------------------- /stripe-payment-link-sms/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /stripe-payment-link-sms/images/stripe-webhook-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/stripe-payment-link-sms/images/stripe-webhook-dashboard.png -------------------------------------------------------------------------------- /stripe-payment-link-sms/images/twilio-console-messaging-webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/stripe-payment-link-sms/images/twilio-console-messaging-webhook.png -------------------------------------------------------------------------------- /stripe-payment-link-sms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-payment-link-sms", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "stripe": "^8.20.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /stripe-sms-receipt/.env: -------------------------------------------------------------------------------- 1 | # description: A Stripe secret key 2 | # format: secret 3 | # link: https://dashboard.stripe.com/test/apikeys 4 | # required: true 5 | STRIPE_SECRET_KEY= 6 | 7 | 8 | # description: Sender phone number. Default is "STRIPEDEMO" 9 | # format: phone_number 10 | # link: https://www.twilio.com/console/phone-numbers/getting-started 11 | # required: false 12 | TWILIO_PHONE_NUMBER= -------------------------------------------------------------------------------- /stripe-sms-receipt/assets/stripe_fixtures/create_customer_and_payment.private.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "template_version": 0 4 | }, 5 | "fixtures": [ 6 | { 7 | "name": "test_customer", 8 | "path": "/v1/customers", 9 | "method": "post", 10 | "params": { 11 | "name": "Jenny Rosen", 12 | "email": "jenny.rosen@example.com", 13 | "phone": "+12025551212" 14 | } 15 | }, 16 | { 17 | "name": "test_payment", 18 | "path": "/v1/payment_intents", 19 | "method": "post", 20 | "params": { 21 | "amount": 1099, 22 | "currency": "usd", 23 | "customer": "${test_customer:id}", 24 | "payment_method": "pm_card_visa", 25 | "off_session": true, 26 | "confirm": true 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /stripe-sms-receipt/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /stripe-sms-receipt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-sms-receipt", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "stripe": "^8.20.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sync-token/.env: -------------------------------------------------------------------------------- 1 | # description: API key for your Twilio Account 2 | # format: text 3 | # link: https://www.twilio.com/console/runtime/api-keys/create 4 | # required: true 5 | API_KEY= 6 | 7 | # description: API secret for your API Key 8 | # format: secret 9 | # link: https://www.twilio.com/console/runtime/api-keys/create 10 | # required: true 11 | API_SECRET= 12 | 13 | # description: SID of your Twilio Sync Service 14 | # format: sid 15 | # link: https://www.twilio.com/docs/api/sync/rest/services 16 | # required: true 17 | SYNC_SERVICE_SID= -------------------------------------------------------------------------------- /sync-token/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /sync-token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /temp-storage/.env: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /temp-storage/README.md: -------------------------------------------------------------------------------- 1 | # Utilise temporary storage under Functions 2 | 3 | This Function shows you how to reach and utilise the temporary storage under the Function layer, mainly for single-invocation jobs 4 | For example, on each invocation we can create a file based on user data and use it accordingly 5 | 6 | IMPORTANT: Do NOT treat this storage as long term storage or for personal data that need to persist. 7 | The contents get deleted whenever the associated container is brought down, so this function is useful for one time actions 8 | 9 | ## Environment variables 10 | 11 | This Function does not require any environment variables to be set 12 | 13 | ## Parameters 14 | 15 | This Function doesn't expect any parameters passed. 16 | -------------------------------------------------------------------------------- /temp-storage/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /temp-storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /transfers/.env: -------------------------------------------------------------------------------- 1 | # Variables for function "" 2 | # --- 3 | # description: Set the password that will allow you to use the application 4 | Password=1 5 | -------------------------------------------------------------------------------- /transfers/.owners: -------------------------------------------------------------------------------- 1 | dsTw5 (dsumberac) 2 | # Insert your Github username here 3 | -------------------------------------------------------------------------------- /transfers/assets/styles.css: -------------------------------------------------------------------------------- 1 | pre { 2 | white-space: pre-wrap; 3 | } 4 | 5 | .alert { 6 | padding: 15px; 7 | margin-top: 10px; 8 | border: 1px solid transparent; 9 | border-radius: 4px; 10 | } 11 | 12 | .alert-info { 13 | border-color: #bce8f1; 14 | color: #31708f; 15 | background-color: #d9edf7; 16 | } 17 | 18 | .alert-error { 19 | color: #a94442; 20 | background-color: #f2dede; 21 | border-color: #ebccd1; 22 | } 23 | -------------------------------------------------------------------------------- /transfers/functions/get_account.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | let finalData = null; 3 | const accountSid = { acc: process.env.ACCOUNT_SID }; 4 | 5 | if (event.request.headers.authorization !== process.env.Password) { 6 | finalData = { er: 0 }; 7 | return callback(null, finalData); 8 | } 9 | try { 10 | return callback(null, accountSid); 11 | } catch (error) { 12 | console.error(error.message); 13 | response.setStatusCode(error.status || 400); 14 | response.setBody({ error: error.message }); 15 | return callback(null, response); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /transfers/functions/mask_account.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const masked = 3 | String(process.env.ACCOUNT_SID).slice(0, 28).replace(/./g, '*') + 4 | String(process.env.ACCOUNT_SID).slice(-6); 5 | const accountSid = { acc: masked }; 6 | 7 | try { 8 | return callback(null, accountSid); 9 | } catch (error) { 10 | console.error(error.message); 11 | response.setStatusCode(error.status || 400); 12 | response.setBody({ error: error.message }); 13 | return callback(null, response); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /transfers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /transfers/tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/transfers/tests/.gitkeep -------------------------------------------------------------------------------- /vaccine-standby/.env: -------------------------------------------------------------------------------- 1 | # description: Your Twilio phone number for sending and receiving messages 2 | # format: phone_number 3 | # required: true 4 | TWILIO_PHONE_NUMBER= 5 | 6 | # description: Set a password for your app. Users who want to configure the app or view who messaged in, will need this password. 7 | # format: secret 8 | # required: true 9 | ADMIN_PASSWORD= 10 | 11 | # description: change the salt to invalidate existing auth tokens 12 | # configurable: false 13 | # format: secret 14 | SALT=salty 15 | 16 | # description: SID of the Vaccine Intake Studio Flow 17 | # configurable: false 18 | # format: sid 19 | FLOW_SID= 20 | -------------------------------------------------------------------------------- /vaccine-standby/assets/standby-list-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/vaccine-standby/assets/standby-list-diagram.png -------------------------------------------------------------------------------- /vaccine-standby/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /vaccine-standby/functions/check-existing-flow.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const client = context.getTwilioClient(); 3 | const flowSid = context.FLOW_SID; 4 | 5 | client.studio.flows 6 | .list({ limit: 100 }) 7 | // eslint-disable-next-line consistent-return 8 | .then((flows) => { 9 | if (flows.length > 0) { 10 | flows.forEach((f) => { 11 | /* 12 | * Note: If you are running this app locally and you kill your dev server after creating 13 | * the Studio Flow via the app, you will need to manually set FLOW_SID in your .env file 14 | * before the next time you start your dev server. 15 | */ 16 | if (f.sid === flowSid) { 17 | return callback(null, f.sid); 18 | } 19 | return callback(null, 'none'); 20 | }); 21 | } else { 22 | return callback(null, 'none'); 23 | } 24 | }) 25 | .catch((err) => callback(err)); 26 | }; 27 | -------------------------------------------------------------------------------- /vaccine-standby/functions/login.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line func-names 2 | exports.handler = function (context, event, callback) { 3 | const { path } = Runtime.getFunctions().auth; 4 | const { createToken, isAllowed } = require(path); 5 | const ac = context.ACCOUNT_SID; 6 | 7 | const token = createToken(event.password, context); 8 | const response = new Twilio.Response(); 9 | response.appendHeader('Content-Type', 'application/json'); 10 | 11 | // Short-circuits 12 | if (isAllowed(token, context)) { 13 | response.setBody({ token }); 14 | callback(null, response); 15 | return; 16 | } 17 | 18 | // eslint-disable-next-line no-undef 19 | response.setStatusCode(401); 20 | response.setBody({ message: 'Unauthorized' }); 21 | 22 | callback(null, response); 23 | }; 24 | -------------------------------------------------------------------------------- /vaccine-standby/functions/return-config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | exports.handler = function (context, event, callback) { 3 | const phone_number = context.TWILIO_PHONE_NUMBER; 4 | const response = new Twilio.Response(); 5 | response.setStatusCode(200); 6 | response.appendHeader('Content-Type', 'application/json'); 7 | response.setBody({ phone_number }); 8 | callback(null, response); 9 | }; 10 | -------------------------------------------------------------------------------- /vaccine-standby/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vaccine-standby", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "twilio": "^3.61.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /verified-broadcast/.env: -------------------------------------------------------------------------------- 1 | # description: The Twilio phone number to send broadcast SMS from 2 | # format: phone_number 3 | # required: true 4 | TWILIO_PHONE_NUMBER= 5 | 6 | # description: Choose a passcode for your app. Users have to use this passcode to send SMS broadcasts or delete all subscriptions 7 | # format: secret 8 | # required: true 9 | PASSCODE= 10 | 11 | # description: SID of a Twilio Messaging Service. Otherwise one will automatically be provisioned for you. 12 | # format: sid 13 | # link: https://console.twilio.com/us1/develop/sms/services 14 | # required: false 15 | MESSAGING_SERVICE_SID= 16 | 17 | # description: SID of your Twilio Notify Service. Otherwise one will automatically be provisioned for you. 18 | # format: sid 19 | # link: https://www.twilio.com/console/notify/services 20 | # required: false 21 | BROADCAST_NOTIFY_SERVICE_SID= 22 | 23 | # description: SID of your Twilio Verify Service. Otherwise one will automatically be provisioned for you. 24 | # format: sid 25 | # link: https://www.twilio.com/console/verify/services 26 | # required: false 27 | VERIFY_SERVICE_SID= 28 | 29 | -------------------------------------------------------------------------------- /verified-broadcast/assets/auth.private.js: -------------------------------------------------------------------------------- 1 | const auth = require('basic-auth'); 2 | const compare = require('tsscmp'); 3 | 4 | const isAuthenticated = (context, event) => { 5 | const { PASSCODE } = context; 6 | 7 | if ( 8 | !event.request || 9 | !event.request.headers || 10 | !event.request.headers.authorization 11 | ) { 12 | return false; 13 | } 14 | 15 | const parsedCredentials = auth.parse(event.request.headers.authorization); 16 | if (!parsedCredentials) { 17 | return false; 18 | } 19 | 20 | const { name, pass } = parsedCredentials; 21 | let valid = true; 22 | 23 | // Simple method to prevent short-circut and use timing-safe compare 24 | valid = compare(name, 'admin') && valid; 25 | valid = compare(pass, PASSCODE) && valid; 26 | 27 | return valid; 28 | }; 29 | 30 | module.exports = { isAuthenticated }; 31 | -------------------------------------------------------------------------------- /verified-broadcast/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.1.0] 6 | ### Added 7 | - Auto provisioning of resources if they are not defined 8 | - Moving from Verify for Admin authorization to passcode based authorization with HTTP Basic Auth 9 | 10 | ## [1.0.0] 11 | ### Added 12 | - Initial release. 13 | 14 | -------------------------------------------------------------------------------- /verified-broadcast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.0", 3 | "private": true, 4 | "dependencies": { 5 | "@twilio/runtime-handler": "1.2.0", 6 | "basic-auth": "^2.0.1", 7 | "tsscmp": "^1.0.6", 8 | "twilio": "^3.67.2" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /verify-dashboard/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify-dashboard/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /verify-dashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /verify-dashboard/tests/service-details.test.js: -------------------------------------------------------------------------------- 1 | const serviceDetailsFunction = require('../functions/service-details').handler; 2 | const helpers = require('../../test/test-helper'); 3 | 4 | const mockService = { 5 | fetch: jest.fn(() => 6 | Promise.resolve({ 7 | sid: 'default', 8 | }) 9 | ), 10 | }; 11 | 12 | const mockClient = { 13 | verify: { 14 | services: jest.fn(() => mockService), 15 | }, 16 | }; 17 | 18 | const testContext = { 19 | VERIFY_SERVICE_SID: 'default', 20 | getTwilioClient: () => mockClient, 21 | }; 22 | 23 | describe('service details', () => { 24 | beforeAll(() => { 25 | helpers.setup({}); 26 | }); 27 | afterAll(() => { 28 | helpers.teardown(); 29 | }); 30 | 31 | test('returns success with valid request', (done) => { 32 | const callback = (_err, result) => { 33 | expect(result).toBeDefined(); 34 | expect(result._body.success).toEqual(true); 35 | expect(mockClient.verify.services).toHaveBeenCalledWith( 36 | testContext.VERIFY_SERVICE_SID 37 | ); 38 | done(); 39 | }; 40 | serviceDetailsFunction(testContext, {}, callback); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /verify-prefill/.env.example: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify-prefill/.owners: -------------------------------------------------------------------------------- 1 | dkundel 2 | alisontanu 3 | pthirumurthi 4 | aricday 5 | -------------------------------------------------------------------------------- /verify-prefill/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /verify-prefill/functions/fetch-user-data.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | 3 | exports.handler = async function (context, event, callback) { 4 | const { phoneNumber, verificationSid } = event; 5 | const lookupApiKey = context.ACCOUNT_SID; 6 | const lookupApiSecret = context.AUTH_TOKEN; 7 | 8 | try { 9 | const lookupUrl = `https://lookups.twilio.com/v2/PhoneNumbers/${phoneNumber}?Fields=pre_fill&VerificationSid=${verificationSid}`; 10 | const lookupResponse = await fetch(lookupUrl, { 11 | headers: { 12 | Authorization: `Basic ${Buffer.from( 13 | // eslint-disable-next-line sonarjs/no-nested-template-literals 14 | `${lookupApiKey}:${lookupApiSecret}` 15 | ).toString('base64')}`, 16 | }, 17 | }); 18 | 19 | const lookupData = await lookupResponse.json(); 20 | return callback(null, { success: true, prefillData: lookupData.pre_fill }); 21 | } catch (error) { 22 | console.error('Error fetching user data:', error); 23 | return callback(null, { success: false, message: error.message }); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /verify-prefill/functions/verify-otp.js: -------------------------------------------------------------------------------- 1 | exports.handler = async function (context, event, callback) { 2 | const { phoneNumber, code } = event; 3 | const { VERIFY_SERVICE_SID: serviceSid } = context; 4 | 5 | try { 6 | const client = context.getTwilioClient(); 7 | 8 | if (!serviceSid) { 9 | throw new Error('Missing VERIFY_SERVICE_SID'); 10 | } 11 | 12 | // Verify the OTP using Twilio Verify API V2 13 | const verificationCheck = await client.verify.v2 14 | .services(serviceSid) 15 | .verificationChecks.create({ to: phoneNumber, code }); 16 | 17 | console.log('Verification check response:', verificationCheck); 18 | 19 | if (verificationCheck.status === 'approved') { 20 | return callback(null, { 21 | success: true, 22 | verificationSid: verificationCheck.sid, 23 | }); 24 | } 25 | 26 | return callback(null, { 27 | success: false, 28 | message: `Verification failed. Status: ${verificationCheck.status}`, 29 | }); 30 | } catch (error) { 31 | console.error('Error verifying OTP:', error); 32 | return callback(null, { success: false, message: error.message }); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /verify-prefill/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/verify-prefill/image.png -------------------------------------------------------------------------------- /verify-prefill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-prefill", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "twilio": "^5.0.4", 7 | "node-fetch": "^2.7.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /verify-push-authy-backend/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= 6 | # description: Define if the identity field will be hashed or will be taken as it is, possible values hash or raw 7 | # format: text 8 | # required: false 9 | IDENTITY_PROCESSING=hash -------------------------------------------------------------------------------- /verify-push-authy-backend/assets/digest-message.private.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | function digestMessage(value) { 4 | return crypto.createHash('sha256').update(value).digest('hex'); 5 | } 6 | 7 | module.exports = { 8 | digestMessage, 9 | }; 10 | -------------------------------------------------------------------------------- /verify-push-authy-backend/assets/missing-params.private.js: -------------------------------------------------------------------------------- 1 | function detectMissingParams(paramNames, event) { 2 | return paramNames.reduce((acc, param) => { 3 | if (typeof event[param] === 'undefined') { 4 | acc.push(param); 5 | } 6 | return acc; 7 | }, []); 8 | } 9 | 10 | module.exports = { 11 | detectMissingParams, 12 | }; 13 | -------------------------------------------------------------------------------- /verify-push-authy-backend/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | -------------------------------------------------------------------------------- /verify-push-authy-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.0", 3 | "private": true, 4 | "dependencies": { 5 | "qrcode": "^1.5.0", 6 | "twilio": "^3.73.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /verify-push-authy-backend/tests/digest-message.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const missing = require('../assets/digest-message.private'); 3 | 4 | describe('verify-push-backend/private/digest-message', () => { 5 | beforeAll(() => { 6 | helpers.setup({}); 7 | }); 8 | afterAll(() => { 9 | helpers.teardown(); 10 | }); 11 | 12 | const { digestMessage } = missing; 13 | 14 | test('Digest a message', () => { 15 | const value = '1234567890'; 16 | const hashedValue = digestMessage(value); 17 | 18 | expect(hashedValue).toEqual( 19 | 'c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646' 20 | ); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /verify-push-backend/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= 6 | # description: Define if the identity field will be hashed or will be taken as it is, possible values hash or raw 7 | # format: text 8 | # required: false 9 | IDENTITY_PROCESSING=hash -------------------------------------------------------------------------------- /verify-push-backend/assets/digest-message.private.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | 3 | function digestMessage(value) { 4 | return crypto.createHash('sha256').update(value).digest('hex'); 5 | } 6 | 7 | module.exports = { 8 | digestMessage, 9 | }; 10 | -------------------------------------------------------------------------------- /verify-push-backend/assets/missing-params.private.js: -------------------------------------------------------------------------------- 1 | function detectMissingParams(paramNames, event) { 2 | return paramNames.reduce((acc, param) => { 3 | if (typeof event[param] === 'undefined') { 4 | acc.push(param); 5 | } 6 | return acc; 7 | }, []); 8 | } 9 | 10 | module.exports = { 11 | detectMissingParams, 12 | }; 13 | -------------------------------------------------------------------------------- /verify-push-backend/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | ## [1.1.0] 10 | ### Changed 11 | - Checking challenge status until its expiration time (5 minutes) 12 | - Clearing factor list container before showing them 13 | 14 | ### Added 15 | - Support for hidden details. 16 | - Env variable to configure identity processing (hashing or not) 17 | - React native link 18 | -------------------------------------------------------------------------------- /verify-push-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /verify-push-backend/tests/digest-message.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | const missing = require('../assets/digest-message.private'); 3 | 4 | describe('verify-push-backend/private/digest-message', () => { 5 | beforeAll(() => { 6 | helpers.setup({}); 7 | }); 8 | afterAll(() => { 9 | helpers.teardown(); 10 | }); 11 | 12 | const { digestMessage } = missing; 13 | 14 | test('Digest a message', () => { 15 | const value = '1234567890'; 16 | const hashedValue = digestMessage(value); 17 | 18 | expect(hashedValue).toEqual( 19 | 'c775e7b757ede630cd0aa1113bd102661ab38829ca52a6422ab782862f268646' 20 | ); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /verify-retry/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify-retry/assets/utils.private.js: -------------------------------------------------------------------------------- 1 | class VerificationException extends Error { 2 | constructor(status, message) { 3 | super(`Error ${status}: ${message}`); 4 | 5 | this.status = status; 6 | this.message = message; 7 | } 8 | } 9 | 10 | function detectMissingParams(paramNames, event) { 11 | return paramNames.reduce((acc, param) => { 12 | if (typeof event[param] === 'undefined') { 13 | acc.push(param); 14 | } 15 | return acc; 16 | }, []); 17 | } 18 | 19 | module.exports = { 20 | VerificationException, 21 | detectMissingParams, 22 | }; 23 | -------------------------------------------------------------------------------- /verify-retry/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /verify-retry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /verify-sna/.env.example: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Account or Subaccount 2 | # format: sid 3 | # link: https://www.twilio.com/console 4 | # required: true 5 | ACCOUNT_SID= 6 | 7 | # description: Auth Token of your Twilio Account or Subaacount 8 | # format: text 9 | # link: https://www.twilio.com/console 10 | # required: true 11 | AUTH_TOKEN= 12 | 13 | # description: SID of your Twilio Verify Service 14 | # format: sid 15 | # link: https://www.twilio.com/console/verify/services 16 | # required: true 17 | VERIFY_SERVICE_SID= 18 | 19 | # description: SID of your Twilio Sync Service 20 | # format: sid 21 | # link: https://www.twilio.com/console/sync/services 22 | # required: true 23 | SYNC_SERVICE_SID= 24 | 25 | # description: SID of the Sync Map in your Sync Service that you want to use for storage 26 | # format: sid 27 | # link: https://www.twilio.com/console/sync/services 28 | # required: true 29 | SYNC_MAP_SID= -------------------------------------------------------------------------------- /verify-sna/.owners: -------------------------------------------------------------------------------- 1 | elkinnarvaez 2 | 3 | # Insert your Github username here 4 | -------------------------------------------------------------------------------- /verify-sna/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /verify-sna/assets/data/index.private.js: -------------------------------------------------------------------------------- 1 | const connectToSyncMap = async (context) => { 2 | const client = context.getTwilioClient(); 3 | return client.sync 4 | .services(context.SYNC_SERVICE_SID) 5 | .syncMaps(context.SYNC_MAP_SID); 6 | }; 7 | 8 | module.exports = { 9 | connectToSyncMap, 10 | }; 11 | -------------------------------------------------------------------------------- /verify-sna/assets/services/constants.private.js: -------------------------------------------------------------------------------- 1 | const PHONE_NUMBER_FIELD = 'phoneNumber'; 2 | const RESOURCE_NOT_FOUND_ERROR_CODE = 20404; 3 | const VERIFIED_STATUS = 'verified'; 4 | const PENDING_STATUS = 'pending'; 5 | const NOT_VERIFIED_STATUS = 'not-verified'; 6 | 7 | module.exports = { 8 | PHONE_NUMBER_FIELD, 9 | RESOURCE_NOT_FOUND_ERROR_CODE, 10 | VERIFIED_STATUS, 11 | PENDING_STATUS, 12 | NOT_VERIFIED_STATUS, 13 | }; 14 | -------------------------------------------------------------------------------- /verify-sna/assets/services/helpers.private.js: -------------------------------------------------------------------------------- 1 | const detectMissingParams = (paramNames, event) => { 2 | return paramNames.reduce((acc, param) => { 3 | if (typeof event[param] === 'undefined') { 4 | acc.push(param); 5 | } 6 | return acc; 7 | }, []); 8 | }; 9 | 10 | const sortVerifications = (verifications) => { 11 | return verifications.sort((a, b) => { 12 | const aDate = new Date(a.dateCreated); 13 | const bDate = new Date(b.dateCreated); 14 | return bDate - aDate; 15 | }); 16 | }; 17 | 18 | module.exports = { 19 | detectMissingParams, 20 | sortVerifications, 21 | }; 22 | -------------------------------------------------------------------------------- /verify-sna/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verify-sna", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "twilio": "^3.61.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /verify-sna/tests/connect-to-sync-map.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('../../test/test-helper'); 2 | 3 | describe('verify-sna/data/index', () => { 4 | beforeAll(() => { 5 | jest.clearAllMocks(); 6 | const runtime = new helpers.MockRuntime(); 7 | helpers.setup({}, runtime); 8 | }); 9 | afterAll(() => { 10 | helpers.teardown(); 11 | }); 12 | beforeEach(() => jest.resetModules()); 13 | 14 | describe('when trying to connect to a sync map with no context', () => { 15 | it('throws an error', async () => { 16 | const { connectToSyncMap } = require('../assets/data/index.private'); 17 | 18 | await expect(connectToSyncMap({})).rejects.toThrowError( 19 | 'context.getTwilioClient is not a function' 20 | ); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /verify-totp-sms/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify-totp-sms/.owners: -------------------------------------------------------------------------------- 1 | robinske 2 | # Insert your Github username here 3 | -------------------------------------------------------------------------------- /verify-totp-sms/assets/utils.private.js: -------------------------------------------------------------------------------- 1 | function detectMissingParams(paramNames, event) { 2 | return paramNames.reduce((acc, param) => { 3 | if (typeof event[param] === 'undefined') { 4 | acc.push(param); 5 | } 6 | return acc; 7 | }, []); 8 | } 9 | 10 | module.exports = { 11 | detectMissingParams, 12 | }; 13 | -------------------------------------------------------------------------------- /verify-totp-sms/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.1] 6 | ### Changed 7 | - Uses Paste theme 8 | - Removes jQuery dependency 9 | - Separate locale dropdown javascript into its own file 10 | - Improves test coverage 11 | 12 | ## [1.0.0] 13 | ### Added 14 | - Initial release. 15 | 16 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('cypress'); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | }; 23 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /verify-totp-sms/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /verify-totp-sms/e2e.js: -------------------------------------------------------------------------------- 1 | const { runE2eTestSuite } = require('../_helpers/test-suite'); 2 | 3 | runE2eTestSuite({ 4 | env: {}, 5 | }); 6 | -------------------------------------------------------------------------------- /verify-totp-sms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.1", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.61.0", 6 | "uuid": "^9.0.0" 7 | }, 8 | "scripts": { 9 | "e2e": "node e2e.js" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /verify-totp/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify-totp/assets/styles.css: -------------------------------------------------------------------------------- 1 | main { 2 | padding-top: 40px; 3 | display: flex; 4 | flex-direction: column; 5 | flex: 1; 6 | justify-content: flex-start; 7 | width: 75%; 8 | margin-left: auto; 9 | margin-right: auto; 10 | } 11 | 12 | div.content { 13 | max-width: 100%; 14 | } 15 | 16 | input[type='submit'] { 17 | font: inherit; 18 | border: 1px solid rgb(136, 145, 170); 19 | border-radius: 4px; 20 | } 21 | 22 | .explainer { 23 | color: #0c5460; 24 | background-color: #d1ecf1; 25 | border-color: #bee5eb; 26 | padding: 0.75rem 1.25rem; 27 | margin-top: 1rem; 28 | border: 1px solid transparent; 29 | border-radius: 0.25rem; 30 | } 31 | 32 | .reset, 33 | #start-backup { 34 | margin-top: 1rem; 35 | } 36 | 37 | .dynamic-render, 38 | .otp-form { 39 | display: none; 40 | } 41 | 42 | #secret { 43 | font-weight: bold; 44 | text-transform: uppercase; 45 | } 46 | 47 | #backup-codes { 48 | font-family: monospace; 49 | } 50 | -------------------------------------------------------------------------------- /verify-totp/assets/utils.private.js: -------------------------------------------------------------------------------- 1 | function detectMissingParams(paramNames, event) { 2 | return paramNames.reduce((acc, param) => { 3 | if (typeof event[param] === 'undefined') { 4 | acc.push(param); 5 | } 6 | return acc; 7 | }, []); 8 | } 9 | 10 | module.exports = { 11 | detectMissingParams, 12 | }; 13 | -------------------------------------------------------------------------------- /verify-totp/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | - Reviewed for accuracy 2024-07-24 5 | 6 | ## [1.0.0] 7 | ### Added 8 | - Initial release 9 | -------------------------------------------------------------------------------- /verify-totp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^3.67.2", 6 | "uuid": "^8.3.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /verify/.env: -------------------------------------------------------------------------------- 1 | # description: SID of your Twilio Verify Service 2 | # format: sid 3 | # link: https://www.twilio.com/console/verify/services 4 | # required: true 5 | VERIFY_SERVICE_SID= -------------------------------------------------------------------------------- /verify/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.1] 6 | ### Changed 7 | - Uses Paste theme 8 | - Removes jQuery dependency 9 | - Separate locale dropdown javascript into its own file 10 | - Improves test coverage 11 | 12 | ## [1.0.0] 13 | ### Added 14 | - Initial release. 15 | 16 | -------------------------------------------------------------------------------- /verify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.2", 3 | "private": true, 4 | "dependencies": { 5 | "twilio": "^5.2.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /verify/phone-verification-v3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/verify/phone-verification-v3.gif -------------------------------------------------------------------------------- /video-token-backend/.env.example: -------------------------------------------------------------------------------- 1 | # description: API key for your Twilio Account 2 | # format: text 3 | # link: https://www.twilio.com/console/runtime/api-keys/create 4 | # required: true 5 | API_KEY_SID= 6 | 7 | # description: API secret for your API Key 8 | # format: secret 9 | # link: https://www.twilio.com/console/runtime/api-keys/create 10 | # required: true 11 | API_KEY_SECRET= 12 | -------------------------------------------------------------------------------- /video-token-backend/.owners: -------------------------------------------------------------------------------- 1 | # Insert your Github username here 2 | PikaJoyce 3 | -------------------------------------------------------------------------------- /video-token-backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /video-token-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "video-token-server", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@twilio-labs/runtime-helpers": "^0.1.2", 7 | "nanoid": "^3.0.0", 8 | "twilio": "^3.82.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /video-token/.env: -------------------------------------------------------------------------------- 1 | # description: API key for your Twilio Account 2 | # format: text 3 | # link: https://www.twilio.com/console/runtime/api-keys/create 4 | # required: true 5 | API_KEY= 6 | 7 | # description: API secret for your API Key 8 | # format: secret 9 | # link: https://www.twilio.com/console/runtime/api-keys/create 10 | # required: true 11 | API_SECRET= -------------------------------------------------------------------------------- /video-token/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /video-token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /video/.env: -------------------------------------------------------------------------------- 1 | # description: API key for your Twilio Account 2 | # format: text 3 | # link: https://www.twilio.com/console/project/api-keys 4 | # required: true 5 | API_KEY= 6 | 7 | # description: API secret for your API Key 8 | # format: secret 9 | # link: https://www.twilio.com/console/project/api-keys 10 | # required: true 11 | API_SECRET= 12 | 13 | # description: Choose a room name for your video call 14 | # format: text 15 | # required: true 16 | ROOM_NAME= 17 | 18 | # description: Choose a passcode for your app. Users have to use this passcode to enter the video call 19 | # format: text 20 | # required: true 21 | PASSCODE= 22 | -------------------------------------------------------------------------------- /video/assets/camera_permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio-labs/function-templates/c5bccee31dc215b40985a0baf05ee99169664f74/video/assets/camera_permissions.png -------------------------------------------------------------------------------- /video/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /video/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "video", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /voice-client-javascript/.env: -------------------------------------------------------------------------------- 1 | # This application automatically initializes a server environment. 2 | # See: /admin/index.html 3 | # Be cautious adding values to this file as they will override the environment's values. 4 | 5 | # description: Choose a name for your application 6 | # format: text 7 | # required: true 8 | APP_NAME=voice-client-javascript 9 | 10 | # description: Set a password for your app. Users who want to use the admin interface will have to use this password to access it 11 | # format: text 12 | # required: true 13 | ADMIN_PASSWORD=default -------------------------------------------------------------------------------- /voice-client-javascript/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /voice-client-javascript/functions/admin/check-status.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const status = require(assets['/admin/statuses.js'].path); 3 | const { checkAuthorization } = require(assets['/admin/shared.js'].path); 4 | const environmentFunction = status.environment; 5 | const statusFunctions = status.statuses; 6 | 7 | async function getStatuses(context) { 8 | // This should be `Promise.allSettled` with a filter, but the node version is off 9 | // eslint-disable-next-line consistent-return 10 | const promises = statusFunctions.map(async (fn) => { 11 | try { 12 | return await fn(context); 13 | } catch (err) { 14 | console.error(`Status check failed for ${fn.name}: ${err}`); 15 | } 16 | }); 17 | const results = await Promise.all(promises); 18 | return results.filter((result) => result !== undefined); 19 | } 20 | 21 | exports.handler = async function (context, event, callback) { 22 | if (!checkAuthorization(context, event, callback)) { 23 | return; 24 | } 25 | const environment = await environmentFunction(context); 26 | const statuses = await getStatuses(context); 27 | callback(null, { 28 | environment, 29 | statuses, 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /voice-client-javascript/functions/admin/login.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const { checkAuthorization, createToken } = require( 3 | assets['/admin/shared.js'].path 4 | ); 5 | 6 | // eslint-disable-next-line consistent-return 7 | exports.handler = function (context, event, callback) { 8 | // Create a token from the password, and use it to check by setting it 9 | // eslint-disable-next-line no-multi-assign 10 | const token = (event.token = createToken(context, event.password)); 11 | // Short-circuits 12 | if (checkAuthorization(context, event, callback)) { 13 | return callback(null, { token }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /voice-client-javascript/functions/client-voice-twiml-app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is valid as phone number 3 | * @param {Number|String} number 4 | * @return {Boolean} 5 | */ 6 | function isAValidPhoneNumber(number) { 7 | return /^[\d\+\-\(\) ]+$/.test(number); 8 | } 9 | 10 | exports.handler = function (context, event, callback) { 11 | const twiml = new Twilio.twiml.VoiceResponse(); 12 | 13 | if (event.To) { 14 | /* 15 | * Wrap the phone number or client name in the appropriate TwiML verb 16 | * if is a valid phone number 17 | */ 18 | const attr = isAValidPhoneNumber(event.To) ? 'number' : 'client'; 19 | 20 | const dial = twiml.dial({ 21 | answerOnBridge: true, 22 | callerId: process.env.CALLER_ID, 23 | }); 24 | dial[attr]({}, event.To); 25 | } else { 26 | twiml.say('Thanks for calling!'); 27 | } 28 | 29 | callback(null, twiml); 30 | }; 31 | -------------------------------------------------------------------------------- /voice-client-javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "common-tags": "^1.8.0", 6 | "twilio": "^3.61.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /voice-ivr/.env: -------------------------------------------------------------------------------- 1 | # description: Your phone number 2 | # format: phone_number 3 | # required: true 4 | MY_PHONE_NUMBER=+12223334444 5 | 6 | # description: The path to the webhook 7 | # configurable: false 8 | TWILIO_VOICE_WEBHOOK_URL=/voice-ivr 9 | -------------------------------------------------------------------------------- /voice-ivr/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | 7 | ### Added 8 | 9 | - Initial release. 10 | -------------------------------------------------------------------------------- /voice-ivr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voice-ivr", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /voice-javascript-sdk/.env: -------------------------------------------------------------------------------- 1 | # This application automatically initializes a server environment. 2 | # See: /admin/index.html 3 | # Be cautious adding values to this file as they will override the environment's values. 4 | 5 | # description: Choose a name for your application 6 | # format: text 7 | # required: true 8 | APP_NAME=voice-javascript-sdk 9 | 10 | # description: Set a password for your app. Users who want to use the admin interface will have to use this password to access it 11 | # format: text 12 | # required: true 13 | ADMIN_PASSWORD=default -------------------------------------------------------------------------------- /voice-javascript-sdk/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | ### Added 7 | - Initial release. 8 | 9 | -------------------------------------------------------------------------------- /voice-javascript-sdk/functions/admin/check-status.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const status = require(assets['/admin/statuses.js'].path); 3 | const { checkAuthorization } = require(assets['/admin/shared.js'].path); 4 | const environmentFunction = status.environment; 5 | const statusFunctions = status.statuses; 6 | 7 | async function getStatuses(context) { 8 | // This should be `Promise.allSettled` with a filter, but the node version is off 9 | // eslint-disable-next-line consistent-return 10 | const promises = statusFunctions.map(async (fn) => { 11 | try { 12 | return await fn(context); 13 | } catch (err) { 14 | console.error(`Status check failed for ${fn.name}: ${err}`); 15 | } 16 | }); 17 | const results = await Promise.all(promises); 18 | return results.filter((result) => result !== undefined); 19 | } 20 | 21 | exports.handler = async function (context, event, callback) { 22 | if (!checkAuthorization(context, event, callback)) { 23 | return; 24 | } 25 | const environment = await environmentFunction(context); 26 | const statuses = await getStatuses(context); 27 | callback(null, { 28 | environment, 29 | statuses, 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /voice-javascript-sdk/functions/admin/login.js: -------------------------------------------------------------------------------- 1 | const assets = Runtime.getAssets(); 2 | const { checkAuthorization, createToken } = require( 3 | assets['/admin/shared.js'].path 4 | ); 5 | 6 | // eslint-disable-next-line consistent-return 7 | exports.handler = function (context, event, callback) { 8 | // Create a token from the password, and use it to check by setting it 9 | // eslint-disable-next-line no-multi-assign 10 | const token = (event.token = createToken(context, event.password)); 11 | // Short-circuits 12 | if (checkAuthorization(context, event, callback)) { 13 | return callback(null, { token }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /voice-javascript-sdk/functions/voice-javascript-sdk-twiml-app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given value is valid as phone number 3 | * @param {Number|String} number 4 | * @return {Boolean} 5 | */ 6 | function isAValidPhoneNumber(number) { 7 | return /^[\d\+\-\(\) ]+$/.test(number); 8 | } 9 | 10 | exports.handler = function (context, event, callback) { 11 | const twiml = new Twilio.twiml.VoiceResponse(); 12 | 13 | if (event.To) { 14 | /* 15 | * Wrap the phone number or client name in the appropriate TwiML verb 16 | * if is a valid phone number 17 | */ 18 | const attr = isAValidPhoneNumber(event.To) ? 'number' : 'client'; 19 | 20 | const dial = twiml.dial({ 21 | answerOnBridge: true, 22 | callerId: process.env.CALLER_ID, 23 | }); 24 | dial[attr]({}, event.To); 25 | } else { 26 | twiml.say('Thanks for calling!'); 27 | } 28 | 29 | callback(null, twiml); 30 | }; 31 | -------------------------------------------------------------------------------- /voice-javascript-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voice-javascript-sdk", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "common-tags": "^1.8.0", 7 | "jsonwebtoken": "^8.5.1", 8 | "twilio": "^3.61.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /voicemail/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] 6 | 7 | ### Added 8 | 9 | - Initial release. 10 | -------------------------------------------------------------------------------- /voicemail/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "dependencies": { 5 | "moment": "^2.30.1" 6 | } 7 | } 8 | --------------------------------------------------------------------------------