├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── .yarn └── releases │ └── yarn-3.3.0.cjs ├── .yarnrc.yml ├── README.md ├── babel.config.js ├── blog ├── 2019-05-28-hola.md ├── 2019-05-29-hello-world.md └── 2019-05-30-welcome.md ├── docs ├── account │ ├── environments.mdx │ ├── environments │ │ ├── env-add.png │ │ └── env-manage.png │ ├── org-members.mdx │ └── org-members │ │ ├── add-member.png │ │ └── manage-members.png ├── advanced-endpoints │ ├── fifo-endpoints.mdx │ ├── intro.mdx │ ├── object-storage.mdx │ └── polling-endpoints.mdx ├── api-keys.mdx ├── app-portal.mdx ├── channels.mdx ├── common-usage-examples.mdx ├── connectors.mdx ├── consuming-webhooks.mdx ├── documenting-webhooks.mdx ├── endpoint-authentication.mdx ├── event-types.mdx ├── get-help.mdx ├── idempotency.mdx ├── img │ ├── advanced-endpoints │ │ ├── advanced-endpoints-setting.png │ │ ├── fifo-endpoint-batching.png │ │ ├── fifo-endpoint-create.png │ │ ├── object-storage-endpoints.png │ │ ├── polling-endpoint-create-key.png │ │ ├── polling-endpoint-create.png │ │ ├── polling-endpoint-details-create-key.png │ │ ├── polling-endpoint-setting.png │ │ └── s3-configuration.png │ ├── api-access-page.png │ ├── attempts.png │ ├── channels.png │ ├── congratulations.png │ ├── connectors-endpoint.png │ ├── connectors-list.png │ ├── connectors │ │ ├── connect-to-discord.png │ │ ├── connect-to-hubspot.png │ │ ├── connect-to-slack.png │ │ ├── connect-to-teams.png │ │ ├── connect-to-windmill.png │ │ ├── custom-integration.png │ │ └── logos │ │ │ ├── closecrm-icon.svg │ │ │ ├── customerio-icon.svg │ │ │ ├── discord-icon.svg │ │ │ ├── hubspot-icon.svg │ │ │ ├── inngest-icon.png │ │ │ ├── loops-icon.svg │ │ │ ├── microsoft-teams-icon.svg │ │ │ ├── resend-icon.svg │ │ │ ├── segment-icon.svg │ │ │ ├── sendgrid-icon.svg │ │ │ ├── slack-icon.svg │ │ │ ├── vercel-icon.svg │ │ │ ├── webhook-icon.svg │ │ │ ├── windmill-icon.svg │ │ │ └── zapier-icon.svg │ ├── dashboard-preview-button.png │ ├── edit-transformation.png │ ├── embedded-management-ui.png │ ├── enable-transformations.png │ ├── endpoint-authentication │ │ ├── endpoint-authentication-configure.png │ │ ├── endpoint-authentication-enable.png │ │ ├── endpoint-configure-mtls.png │ │ └── endpoint-configure-oauth.png │ ├── entity-graph.svg │ ├── env-specific-settings.png │ ├── event-catalog-config.png │ ├── event-catalog-published.png │ ├── event-type-openapi.png │ ├── event-type-selection.png │ ├── import-export.png │ ├── msg-flow.svg │ ├── ngrok-signing-key.png │ ├── ngrok-webhook-provider.png │ ├── opentelemetry-spans.png │ ├── schema-add-code.png │ ├── schema-add-schema.png │ ├── schema-editor-basic.png │ ├── schema-preview.png │ ├── slack-portal-connect.png │ ├── slack-portal.png │ ├── svix-play.png │ ├── transformations-card.png │ ├── tutorials │ │ └── slack-connectors │ │ │ ├── advanced-formatting.png │ │ │ ├── new-endpoint.png │ │ │ ├── slack-notification.png │ │ │ ├── testing.png │ │ │ └── with-without-connectors.svg │ ├── zap-creation-step-1.png │ ├── zap-creation-step-2.png │ ├── zap-creation-step-3.1.png │ ├── zap-creation-step-3.png │ ├── zap-creation-step-4.png │ ├── zap-creation-step-5.png │ └── zapier-integration-generator.png ├── incoming-webhooks.mdx ├── integrations │ ├── advanced-zapier.mdx │ ├── ngrok.mdx │ └── zapier.mdx ├── introduction.mdx ├── onboarding.mdx ├── opentelemetry-streaming.mdx ├── overview.mdx ├── play.mdx ├── quickstart.mdx ├── rate-limit.mdx ├── receiving │ ├── additional-authentication.mdx │ ├── introduction.mdx │ ├── receiving-with-ingest.mdx │ ├── source-ips.mdx │ ├── using-app-portal │ │ ├── adding-endpoints.mdx │ │ ├── event-catalog.mdx │ │ ├── filtering-logs.mdx │ │ ├── polling-endpoints.mdx │ │ ├── replaying-messages.mdx │ │ └── testing-events.mdx │ └── verifying-payloads │ │ ├── how-manual.mdx │ │ ├── how.mdx │ │ ├── receiving-with-bridge.mdx │ │ └── why.mdx ├── retention.md ├── retries.mdx ├── security.mdx ├── sending-messages-with-bridge.mdx ├── transformations.mdx └── tutorials │ ├── api-webhook-management.md │ ├── cli.mdx │ ├── connectors.mdx │ └── event-type-schema.mdx ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src ├── components │ └── ConnectorLogo.js ├── css │ └── custom.css └── theme │ ├── CLITabs.js │ ├── CodeTabs.js │ └── EventTypeTags.js ├── static ├── .nojekyll ├── img │ ├── app-portal │ │ ├── add-endpoint.png │ │ ├── date-filter.png │ │ ├── embedded-view-screen.png │ │ ├── embedded.png │ │ ├── endpoint.png │ │ ├── event-catalog.png │ │ ├── log-list.png │ │ ├── replay-modal.png │ │ ├── resend-single.png │ │ └── testing-endpoint.png │ ├── brand.svg │ ├── brand.white.svg │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── ingest │ │ ├── destination-endpoints.png │ │ ├── destination-logs.png │ │ ├── source-edit.png │ │ └── url-token-rotate.png │ ├── logo.svg │ └── socialbanner.png ├── js │ └── segment.js └── webhook-ips.json ├── tsconfig.json └── vercel.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @svix/Engineering 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | typos: 11 | name: Check for typos 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: crate-ci/typos@v1.27.3 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | Session.vim 23 | 24 | .idea 25 | 26 | # yarn 27 | /.yarn/cache 28 | /.yarn/install-state.gz 29 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-3.3.0.cjs 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

Svix - Webhooks as a service

5 |
6 |

7 | 8 | Documentation for the Svix libraries and CLI tool 9 | 10 | [![Join our slack](https://img.shields.io/badge/Slack-join%20the%20community-blue?logo=slack&style=social)](https://www.svix.com/slack/) 11 | 12 | # Documentation 13 | 14 | The docs are deployed to https://docs.svix.com 15 | 16 | ## Installation 17 | 18 | ```console 19 | yarn install 20 | ``` 21 | 22 | ## Local Development 23 | 24 | ```console 25 | yarn start 26 | ``` 27 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /blog/2019-05-28-hola.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hola 3 | title: Hola 4 | author: Gao Wei 5 | author_title: Docusaurus Core Team 6 | author_url: https://github.com/wgao19 7 | author_image_url: https://avatars1.githubusercontent.com/u/2055384?v=4 8 | tags: [hola, docusaurus] 9 | --- 10 | 11 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 12 | -------------------------------------------------------------------------------- /blog/2019-05-29-hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hello-world 3 | title: Hello 4 | author: Endilie Yacop Sucipto 5 | author_title: Maintainer of Docusaurus 6 | author_url: https://github.com/endiliey 7 | author_image_url: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4 8 | tags: [hello, docusaurus] 9 | --- 10 | 11 | Welcome to this blog. This blog is created with [**Docusaurus 2 alpha**](https://v2.docusaurus.io/). 12 | 13 | 14 | 15 | This is a test post. 16 | 17 | A whole bunch of other information. 18 | -------------------------------------------------------------------------------- /blog/2019-05-30-welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | author: Yangshun Tay 5 | author_title: Front End Engineer @ Facebook 6 | author_url: https://github.com/yangshun 7 | author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 8 | tags: [facebook, hello, docusaurus] 9 | --- 10 | 11 | Blog features are powered by the blog plugin. Simply add files to the `blog` directory. It supports tags as well! 12 | 13 | Delete the whole directory if you don't want the blog features. As simple as that! 14 | -------------------------------------------------------------------------------- /docs/account/environments.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Managing Environments 3 | --- 4 | 5 | This section explains how to add and use multiple environments (e.g. dev, staging, and prod) from the same account. 6 | 7 | ## What are environments 8 | 9 | It's common practice for engineering teams to have multiple environments, such as development, staging, and production. The idea behind having multiple environments is to be able to test changes in isolation and catch issues before they hit production. So for example, a developer may make some changes locally, test them, and push them for testing in CI/CD. Following that, the change will be moved to a shared `development` environment where it would be tested by the wider team. Changes that pass this level of testing will move to `staging`, where they are deployed to an environment that's as similar as possible to the `production` environment, and after all testing pass there they would be deployed to `production` and rolled out to users. 10 | 11 | Not all teams go through all of the steps above (and some go through more), but the idea remains the same: you want to isolate your `production` environment, where real user data lies, from your non-production environments that may contain bugs and regressions. 12 | 13 | ## Managing environments 14 | 15 | Your Svix environments are completely isolated and have separate data, settings, and API keys. 16 | 17 | You can create as many environments as you want from the [Environment Management page](https://dashboard.svix.com/environments) on the dashboard. 18 | 19 | ### Add environments 20 | 21 | Click on `New Environment`, choose a name, mark the environment as either `Development` or `Production`, and choose the wanted region. 22 | 23 | ![New environment](./environments/env-add.png) 24 | 25 | Development and production environments function identically; however, you will only be able to preview the App Portal for your development environments. 26 | 27 | ![Preview App Portal](../img/dashboard-preview-button.png) 28 | 29 | ### View environments 30 | 31 | This is how it looks like after adding multiple environments: 32 | 33 | ![Manage environments](./environments/env-manage.png) 34 | 35 | You can then switch between the active environment using the switcher on the top left corner and just start using that environment like you used your account before. 36 | 37 | ### Environment-specific settings 38 | 39 | When configuring your environments, it's important to know which settings are shared across your environments and which are kept separate. On the settings page, there is a separate section for "environment-specific" settings. 40 | 41 | ![environment specific settings](../img/env-specific-settings.png) 42 | 43 | #### App Portal White Labeling 44 | 45 | The App Portal [White Labeling](../app-portal#white-labeling) is configured separately for each environment. This allows you to experiment with how the App Portal will look in your development environment without affecting your production users. 46 | 47 | #### HTTPS Only Endpoints 48 | 49 | It is strongly recommended that all registered endpoints in production environments are secured with HTTPS. If you want to allow unsecure endpoints (HTTP) on any environment, you may do so on the "General Settings" page. 50 | 51 | As of October 1st, 2021, HTTPS Only Endpoints is enabled by default on newly created environments. If your environment was created before then, the default was disabled and your environment was left unchanged. 52 | 53 | 54 | ## Environment import & export 55 | 56 | Import and export makes it very easy to clone (or sync) environments, and thus make sure that your staging and production environments are identical. It also makes it possible to save your environment settings in version control in order to make sure you keep track of changes to your environment. 57 | 58 | Export currently supports the environment settings as well as all of the event types. To import and export, all you need to do is to go to the [environment management](https://dashboard.svix.com/environments) page on the dashboard, choose the wanted environment from the list, and then click either import or export. 59 | 60 | ![Import and export from an environment](../img/import-export.png) 61 | -------------------------------------------------------------------------------- /docs/account/environments/env-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/account/environments/env-add.png -------------------------------------------------------------------------------- /docs/account/environments/env-manage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/account/environments/env-manage.png -------------------------------------------------------------------------------- /docs/account/org-members.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Managing Member Access 3 | --- 4 | 5 | This section explains how to manage access to your organization's account. You can easily give your teammates access, manage it, and invite new team members. 6 | 7 | ## Managing members 8 | 9 | There are currently four supported roles: `Viewer`, `Support Agent`, `Member` and `Admin`. 10 | - A `Viewer` has basic privileges and can view statistics and basic configurations but can't access API keys. 11 | - A `Support Agent` can preview [Consumer App Portals](../app-portal), in addition to having the same permissions as a `Viewer`. 12 | - A `Member` can control most aspects of the account, including applications, endpoints and event types. 13 | - An `Admin` can do all of that, and also manage members. 14 | 15 | You can invite as many members as you want to your organization, so feel free to invite all of your team. To do it, go to the [organization members page](https://dashboard.svix.com/settings/organization-group/members) on the dashboard. 16 | 17 | ### Invite a user 18 | 19 | Click on `Invite Teammates`, put in the email address of the teammate you would like to invite, and choose the appropriate role. 20 | 21 | ![New member](./org-members/add-member.png) 22 | 23 | Svix will then send an invitation to the provided email address. 24 | 25 | ### Accept an invitation 26 | 27 | Once your teammate receives the invitation via email, they can follow the link to decide whether to accept or decline the invitation. Accepting the invitation will remove the recipient from their current account and add them to the new account. Declining the invitation will expire it. 28 | 29 | The recipient's email must be verified before they can accept an invitation. 30 | 31 | ### View members 32 | 33 | This is how it looks like after you've sent some invitations: 34 | 35 | ![Manage members](./org-members/manage-members.png) 36 | 37 | You can then add more members, manage access and manage invitations directly from this page. 38 | -------------------------------------------------------------------------------- /docs/account/org-members/add-member.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/account/org-members/add-member.png -------------------------------------------------------------------------------- /docs/account/org-members/manage-members.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/account/org-members/manage-members.png -------------------------------------------------------------------------------- /docs/advanced-endpoints/fifo-endpoints.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: FIFO Endpoints 3 | --- 4 | 5 | # FIFO Endpoints 6 | 7 | FIFO endpoints let your webhook consumers receive webhooks in strict FIFO ordering (first in first out), unlike regular webhooks, which are delivered independently and order is on a best effort basis. 8 | 9 | Svix supports sending webhooks to both FIFO and regular endpoints, with no code changes required on your end. 10 | 11 | :::info[Why not make all webhooks FIFO?] 12 | Ensuring strict FIFO ordering comes with some tradeoffs. Since every call to the receiver endpoint is blocked until the previous one is successful, throughput is limited compared to regular webhook endpoints. 13 | 14 | Read more about [how FIFO endpoints work](https://www.svix.com/blog/fifo-ordered-webhooks-delivery/) and [the challenges with guaranteeing webhook ordering](https://www.svix.com/blog/guaranteeing-webhook-ordering/). 15 | ::: 16 | 17 | 18 | ## Enabling FIFO Endpoints 19 | FIFO Endpoints can be enabled at the environment level in the [Svix Dashboard](https://dashboard.svix.com/settings/organization/general-settings) by enabling **Advanced Endpoint Types**. 20 | 21 | ![Enable FIFO Endpoints](../img/advanced-endpoints/advanced-endpoints-setting.png) 22 | 23 | 24 | When you enable FIFO Endpoints, your users will be able to create them in the [App Portal](/app-portal). 25 | 26 | ![Polling Endpoint Create](../img/advanced-endpoints/fifo-endpoint-create.png) 27 | 28 | ## Message Batching 29 | Because of the strict ordering, Svix has to wait for a successful acknowledgement of delivery before sending any other messages. To address throughput constraints FIFO endpoints deliver webhooks in configurable batch sizes. 30 | 31 | The batching parameters can be configured in the [App Portal](/app-portal) for each endpoint. 32 | 33 | ![FIFO Endpoint Batching](../img/advanced-endpoints/fifo-endpoint-batching.png) 34 | 35 | ## Transformations 36 | [Transformations](/transformations) are also supported on FIFO endpoints. For FIFO endpoints, transformations are applied to each batch of messages. 37 | 38 | ### How to write a FIFO endpoint transformation 39 | Svix expects a Transformation to declare a function named `handler`. Svix will pass a `InputObject` to this function as its only argument, and expects the function to always return an `OutObject`. 40 | 41 | `InputObject` is a JSON object containing one property: 42 | 43 | - `events`, an array of objects containing `payload` and `eventType` properties. There is one element in the array for each message in the batch. 44 | 45 | `OutObject` is a JSON object containing one property: 46 | 47 | - `requestBody`, a string with the raw request body to be sent to the endpoint. 48 | 49 | ### An example Transformation 50 | 51 | You can write a transformation that transforms certain messages in the batch and forwards them. 52 | 53 | ```js 54 | function handler(input) { 55 | const events = input.events.map((evt) => { 56 | if (evt.eventType === "user.created") { 57 | return { 58 | ...evt.payload, 59 | name: evt.payload.firstName + " " + evt.payload.lastName 60 | } 61 | } 62 | }); 63 | 64 | return { 65 | requestBody: JSON.stringify({ data: events }) 66 | } 67 | } 68 | ``` 69 | 70 | Or you can write transformations that collect messages and returns a summarized response. 71 | 72 | ```js 73 | function handler(input) { 74 | const total = input.events.reduce((acc, evt) => { 75 | if (evt.eventType === "invoice.created") { 76 | return acc + evt.payload.amount; 77 | } 78 | return acc; 79 | }, 0); 80 | 81 | return { 82 | requestBody: JSON.stringify({ total }) 83 | } 84 | } 85 | ``` -------------------------------------------------------------------------------- /docs/advanced-endpoints/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Endpoint Types 3 | --- 4 | 5 | # Advanced Endpoint Types 6 | 7 | Svix supports multiple types of endpoints in addition to regular webhook endpoints, to support different use cases. 8 | 9 | - [Polling Endpoints](/advanced-endpoints/polling-endpoints) 10 | - [FIFO Endpoints](/advanced-endpoints/fifo-endpoints) 11 | - [Object Storage](/advanced-endpoints/object-storage) 12 | 13 | ## Enabling Advanced Endpoint Types 14 | 15 | Advanced Endpoint Types can be enabled at the environment level in the [Svix Dashboard](https://dashboard.svix.com/settings/organization/general-settings). 16 | 17 | ![Enable Advanced Endpoint Types](../img/advanced-endpoints/advanced-endpoints-setting.png) 18 | 19 | When you enable Advanced Endpoint Types, your users will be able to create them in the [App Portal](/app-portal). 20 | 21 | ![Advanced Endpoint Types](../img/advanced-endpoints/object-storage-endpoints.png) 22 | -------------------------------------------------------------------------------- /docs/advanced-endpoints/object-storage.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Object Storage 3 | --- 4 | 5 | # Object Storage Endpoints 6 | 7 | Svix supports sending messages directly to [AWS S3](https://aws.amazon.com/s3/), [Google Cloud Storage](https://cloud.google.com/storage) and [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs). 8 | 9 | This is useful for storing payloads in a cloud storage bucket without your customers having to set up any listener endpoint or write any glue code. 10 | 11 | When **Advanced Endpoint Types** is [enabled](/advanced-endpoints/intro#enabling-advanced-endpoint-types), your customers will see the option to use object storage destinations in the [App Portal](/app-portal). 12 | 13 | ![Object Storage Endpoint Create](../img/advanced-endpoints/object-storage-endpoints.png) 14 | 15 | They will be able to configure the connection right in the App Portal. 16 | 17 | ![Object Storage Endpoint Create](../img/advanced-endpoints/s3-configuration.png) -------------------------------------------------------------------------------- /docs/advanced-endpoints/polling-endpoints.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Polling Endpoints 3 | --- 4 | 5 | # Polling Endpoints 6 | 7 | Polling Endpoints are a way for your users to get a stream of events by polling instead of listening to webhooks. 8 | 9 | ## When is polling better? 10 | 11 | There are a few examples where polling for events works better than webhooks. 12 | 13 | One example is when testing webhooks locally. It's much easier to poll for events than exposing a public HTTP endpoint (even with tools like [Svix Play](/play) or [ngrok](/integrations/ngrok)). 14 | 15 | Another example is when your users don't care about getting events in real-time and prefer getting them all at once at the end of a day. One place where this use-case comes up, is when your users need to store all events for compliance reasons. In that case batching and saving them all at once is easier and more cost efficient for them. 16 | 17 | ## Enabling Polling Endpoints 18 | 19 | Polling Endpoints can be enabled at the environment level in the [Svix Dashboard](https://dashboard.svix.com/settings/organization/general-settings) by enabling **Advanced Endpoint Types**. 20 | 21 | ![Enable Polling Endpoints](../img/advanced-endpoints/advanced-endpoints-setting.png) 22 | 23 | When you enable Polling Endpoints, your users will be able to create them in the [App Portal](/app-portal). 24 | 25 | ![Polling Endpoint Create](../img/advanced-endpoints/polling-endpoint-create.png) 26 | 27 | ## Usage 28 | Like with webhook endpoints, Polling Endpoints support filtering messages by [event types](/event-types) and [channels](/channels). 29 | 30 | In the App Portal, event consumers will get a unique URL and an API key to iterate through the full list of events sent to their [Svix Application](/overview#consumer-applications) since the endpoint was created. 31 | 32 | When creating a Polling Endpoint, consumers will get a unique URL like `https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q` 33 | 34 | When first calling the endpoint, it will return an empty array and an iterator. 35 | 36 | ```json 37 | { 38 | "data": [], 39 | "iterator": "eyJvZmZzZXQiOi05MjIzMzcyMDM2ODU0Nzc1ODA4LCJhZnRlciI6bnVsbH0", 40 | "done": false 41 | } 42 | ``` 43 | 44 | Then, using the iterator in the next call, they can iterate through the events and poll for new ones. 45 | 46 | ```bash 47 | curl \ 48 | "https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q?iterator=eyJv..." \ 49 | -H 'Accept: application/json' \ 50 | -H 'Authorization: Bearer sk_poll_*****.eu' 51 | ``` 52 | 53 | Messages will be returned in the order they were sent to Svix. 54 | 55 | ### Polling with a Server-tracked Iterator 56 | 57 | By polling with a **Consumer ID**, the server will track the client's progress as it iterates through the stream of 58 | messages. 59 | 60 | > Consumer IDs are arbitrary strings used by a client to self-identify. 61 | > 62 | > Note that Consumer IDs should be _unique per client_ and should not be shared. Put another way, Consumer IDs track 63 | > exclusive sequential access through the stream of messages. 64 | > Shared concurrent access through a given Consumer ID will result in errors as mutual clients drift in their positions 65 | > in the stream. 66 | 67 | Usage is the same, and still relies on sending the `iterator` parameter to move forward through the stream. 68 | Server tracking of the client's iterator is opt-in by adding a trailing `/consumer/CONSUMER_ID` to the Polling Endpoint 69 | URL. 70 | 71 | ```bash 72 | curl \ 73 | "https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q/consumer/MY-CONSUMER-ID?iterator=eyJv..." \ 74 | -H 'Accept: application/json' \ 75 | -H 'Authorization: Bearer sk_poll_*****.eu' 76 | ``` 77 | 78 | The big difference is the server "remembers" the last `iterator` used to make a request for the given Consumer ID 79 | such that subsequent requests _without a `iterator`_ will resume using that last seen value. 80 | This is useful for situations where a process may be interrupted and lose its place while polling. 81 | 82 | In summation, when using Consumer IDs you need to provide an `iterator` in order to move forward, but you don't have to 83 | worry about losing progress or starting over if you lose an iterator for any reason. 84 | Poll the stream using the Consumer ID, omitting the `iterator` parameter, and you'll pick up where you left off. 85 | At that stage, use the provided `iterator` given in the response to start moving forward again. 86 | 87 | ### Updating a Server-tracked Iterator with Seek 88 | 89 | The `iterator` persisted on the server side can be adjusted with the `seek` endpoint. 90 | 91 | ```bash 92 | curl \ 93 | "https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q/consumer/MY-CONSUMER-ID/seek" \ 94 | -H 'Accept: application/json' \ 95 | -H 'Content-Type: application/json' \ 96 | -H 'Authorization: Bearer sk_poll_*****.eu' \ 97 | -d '{"after": "2025-01-17T00:00:00.0Z"}' 98 | ``` 99 | 100 | Note that after performing a `seek`, active clients sending a now-out-of-sequence `iterator` may see errors. 101 | In this situation, omitting the `iterator` parameter (as if making a new "initial" request) will allow such clients 102 | to pick up from the position tracked by the server. Subsequent requests should then use the iterator value returned 103 | in each response body. 104 | 105 | --- 106 | 107 | Polling Endpoints can be used in parallel with regular Svix webhooks, and you don't need to make any changes to how you [create messages](/quickstart#send-a-message). 108 | -------------------------------------------------------------------------------- /docs/api-keys.mdx: -------------------------------------------------------------------------------- 1 | # API keys 2 | 3 | Manage your API keys to authenticate requests with Svix. 4 | Svix authenticates your API requests using your account’s API keys. If you don’t include your key when making an API request, or use an incorrect or outdated one, Svix returns an error. 5 | 6 | **API keys are per environment.** Every organization starts with a development environment with a corresponding API key. This key should only be used for development or internal testing and is not intended to be used in any production systems. 7 | 8 | ## Obtaining your API keys 9 | 10 | Your API keys can be found on the "API Access" page of the Svix Dashboard. 11 | 12 | ![API Access page](./img/api-access-page.png) 13 | 14 | ## Keeping your keys safe 15 | 16 | Your secret API key can be used to make any API call on behalf of your account. Treat your secret API key as you would any other password. Grant access only to those who need it. Ensure it is kept out of any version control system you may be using. Control access to your key using a password manager or secrets management service. 17 | 18 | ## Rotating keys 19 | 20 | Svix support creating multiple API keys per environment. This gives you a lot of flexibility when rotating keys. For example, you can create a new key, replace all instances of the old key, and then expire the old key for a zero downtime key rotation. 21 | 22 | When expiring keys, you can also define when you would like the key to expire. Either immediately, or at a later point in time. 23 | -------------------------------------------------------------------------------- /docs/channels.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Channels 3 | --- 4 | 5 | Channels are an extra dimension of filtering messages that is orthogonal to [event types](./event-types.mdx). You can listen to multiple channels from each endpoint, and you can send each message to multiple channels. 6 | 7 | Event types imply a specific consistent schema, and mean a specific type of message. Channels are filters based on the expected recipient or group of recipients. 8 | 9 | **Note:** like event types, channels are meant to filter messages to a particular application, and not across applications. 10 | 11 | ## How to use it 12 | 13 | You first need to enable support to it for each of your environments from [the dashboard](https://dashboard.svix.com/settings/organization/general-settings). 14 | 15 | Once enabled, your customers can choose their wanted channels from the Application Portal, or alternatively you can set it per endpoint in the API. 16 | 17 | ![Add endpoint with channels](./img/channels.png) 18 | 19 | You then need to send messages with the corresponding channels in order for these to reach the endpoints that filter by them. See below for the channels filtering rules. 20 | 21 | ```typescript 22 | await svix.message.create('app_id', { 23 | eventType: "user.signup", 24 | channels: [ 25 | "project_123", 26 | "project_group_11" 27 | ], 28 | payload: { 29 | "username": "test_user", 30 | "email": "test@example.com" 31 | }, 32 | }); 33 | ``` 34 | 35 | ## When not to use it 36 | 37 | The channels are not meant as a way to filter messages across different customers, that's what applications are for. Applications provide proper isolation between messages of different customers, while channels do not. Additionally, while you can have an infinite number of applications, there's a limit to the number of endpoints per application (more on that [on the Endpoint API docs](https://api.svix.com/docs#tag/Endpoint)). 38 | 39 | Additionally, there are limits to the number of channels messages and endpoints can be associated with. And having applications with a large number of endpoints is very inefficient and can hurt performance. 40 | 41 | ## Example use-cases 42 | 43 | Channels are useful for when you have a variety of sub-categories or recipients that expect the same types of messages but just need additional filtering. 44 | 45 | For example, consider Github. You may want to define webhooks for the whole organization, but only send certain events to certain endpoints based on the repository. You could just create a Svix App per repository and then manually add the endpoints to each, but it makes for a much better experience to have the webhook handling defined in one place with the same endpoints listening to multiple projects. 46 | 47 | So for example, you can have `svix`, `svix/svix-webhooks` and `svix/svix-docs` as channels, and then have Github send messages for both `svix` and for each repo whenever an event occurs on a specific repository. Github's customers can then create endpoints that listen to events either for the whole group, or for each repository in particular. 48 | 49 | ## Channels filtering rules 50 | 51 | Channels are case-sensitive, and endpoints that are filtering for specific channels will only get messages sent to that specific channel. 52 | 53 | Svix will send (or not send) to endpoints based on the following conditions: 54 | 1. Endpoint has no channels set: this is a catch-all, all messages are sent to to it, regardless of whether the message had channels set. 55 | 2. Both endpoint and message have channels set: if there's a shared channel between them, the message will be sent to the endpoint. 56 | 3. Endpoint has channels set and message has *no* channels set: the message will not be sent to the endpoint. 57 | -------------------------------------------------------------------------------- /docs/consuming-webhooks.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Consuming Webhooks 3 | --- 4 | 5 | In addition to helping you send webhooks to your users, we also help your users to easily verify the authenticity and security of the webhooks they receive from you. 6 | 7 | :::info 8 | For information on how to verify webhooks, please head to the [Consuming Webhooks documentation](/receiving/introduction.mdx). 9 | ::: 10 | 11 | ## Documentation for your users 12 | 13 | We offer easy to use docs for how to safely consume webhooks which you can share with your users directly: [Consuming Webhooks documentation](/receiving/introduction.mdx). 14 | 15 | ## Building your own libraries 16 | 17 | Depending on your product, you may want to offer additional processing before passing the verified payload for your customers. For example, you may want to create API objects from the payload that your users can use to interact with your API. 18 | 19 | In this scenario you would want to create your own `Webhook` class equivalent that uses the `Svix` class internally. This way you can get all of the verification that Svix offer, while still being able to post-process the payload before passing it to your users. 20 | -------------------------------------------------------------------------------- /docs/endpoint-authentication.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Endpoint Authentication 3 | --- 4 | 5 | Svix supports advanced endpoint authentication methods that can be used by your customers on top of the standard signature verification. 6 | 7 | :::info 8 | These are advanced methods that are not required to keep your webhooks secure, but your customers might need them for their use case. 9 | Read the [security docs](/security) for more information. 10 | ::: 11 | 12 | ## Enabling OAuth and mTLS 13 | 14 | OAuth and mTLS can be enabled at the environment level in the [Svix Dashboard](https://dashboard.svix.com/settings/organization/general-settings). 15 | 16 | ![Enable Endpoint Authentication](./img/endpoint-authentication/endpoint-authentication-enable.png) 17 | 18 | When enabled, your users will see an option in the [App Portal](/app-portal) to configure the respective authentication method on their endpoints. 19 | 20 | ![Endpoint Authentication Configure](./img/endpoint-authentication/endpoint-authentication-configure.png) 21 | 22 | ## OAuth 23 | To configure OAuth, your users will need to enter a `Client ID` and the `Authorization Server URL`, as well as the rest of the OAuth parameters, depending on the desired `Grant type` and `Authentication method`. 24 | 25 | ![OAuth Authentication](./img/endpoint-authentication/endpoint-configure-oauth.png) 26 | 27 | ## Mutual TLS (mTLS) 28 | 29 | With mTLS, your users can upload a private PEM encoded private key and certificate, which will be used to sign the requests sent to the endpoint, and to verify the identity of the receiving server. 30 | 31 | ![Mutual TLS Authentication](./img/endpoint-authentication/endpoint-configure-mtls.png) 32 | 33 | For self-signed certificates, a custom Certificate Authority certificate can also be specified. 34 | -------------------------------------------------------------------------------- /docs/get-help.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get Help & Chat With Us 3 | --- 4 | 5 | We love chatting with developers! 6 | 7 | If anything is unclear, you have some feedback, or you would just like to chat please get in touch! You can join the community on [our Slack](https://www.svix.com/slack/) or reach out via email at contact@svix.com. We are also happy to jump on a call, so just drop us a line. 8 | 9 | [![Join our slack](https://img.shields.io/badge/Slack-join%20the%20community-blue?logo=slack&style=social)](https://www.svix.com/slack/) 10 | -------------------------------------------------------------------------------- /docs/idempotency.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Idempotency 3 | --- 4 | 5 | import CodeTabs from "@theme/CodeTabs"; 6 | import TabItem from "@theme/TabItem"; 7 | 8 | Svix supports [idempotency](https://en.wikipedia.org/wiki/Idempotence) for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit and you do not receive a response. 9 | 10 | To perform an idempotent request, pass the idempotency key in the `Idempotency-Key` header to the request. The idempotency key should be a unique value generated by the client. You can create the key in however way you like, though we suggest using UUID v4, or any other string with enough entropy to avoid collisions. 11 | 12 | 13 | 14 | 15 | ```js 16 | const svix = new Svix("AUTH_TOKEN"); 17 | const message = { 18 | eventType: "invoice.paid", 19 | eventId: "evt_Wqb1k73rXprtTm7Qdlr38G", 20 | payload: { 21 | type: "invoice.paid", 22 | id: "invoice_WF7WtCLFFtd8ubcTgboSFNql", 23 | status: "paid", 24 | attempt: 2, 25 | }, 26 | }; 27 | await svix.message.create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", message, { 28 | idempotencyKey: "fd56a56b-838d-4456-8b83-390802672895", 29 | }); 30 | ``` 31 | 32 | 33 | 34 | 35 | ```python 36 | svix = Svix("AUTH_TOKEN") 37 | message = MessageIn( 38 | event_type="invoice.paid", 39 | event_id="evt_Wqb1k73rXprtTm7Qdlr38G", 40 | payload={ 41 | "type": "invoice.paid", 42 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 43 | "status": "paid", 44 | "attempt": 2 45 | } 46 | ) 47 | svix.message.create( 48 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0", 49 | message, 50 | PostOptions(idempotency_key="fd56a56b-838d-4456-8b83-390802672895") 51 | ) 52 | ``` 53 | 54 | 55 | 56 | 57 | ```go 58 | svixClient := svix.New("AUTH_TOKEN", nil) 59 | eventId := "evt_Wqb1k73rXprtTm7Qdlr38G" 60 | message := svix.MessageIn{ 61 | EventType: "invoice.paid", 62 | EventId: *svix.NullableString(&eventId), 63 | Payload: map[string]interface{}{ 64 | "type": "invoice.paid", 65 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 66 | "status": "paid", 67 | "attempt": 2, 68 | }, 69 | } 70 | idempotencyKey := "fd56a56b-838d-4456-8b83-390802672895" 71 | svixClient.Message.CreateWithOptions(ctx, "app_Xzx8bQeOB1D1XEYmAJaRGoj0", &message, &svix.PostOptions{ 72 | IdempotencyKey: &idempotencyKey, 73 | }) 74 | ``` 75 | 76 | 77 | 78 | 79 | ```rust 80 | let svix = Svix::new("AUTH_TOKEN".to_owned(), None); 81 | svix.message() 82 | .create( 83 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0".to_owned(), 84 | MessageIn { 85 | event_type: "invoice.paid".to_owned(), 86 | event_id: Some("evt_Wqb1k73rXprtTm7Qdlr38G".to_owned()), 87 | payload: json!({ 88 | "type": "invoice.paid", 89 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 90 | "status": "paid", 91 | "attempt": 2 92 | }), 93 | ..MessageIn::default() 94 | }, 95 | Some(PostOptions { 96 | idempotency_key: Some("fd56a56b-838d-4456-8b83-390802672895".to_owned()), 97 | }), 98 | ) 99 | .await?; 100 | ``` 101 | 102 | 103 | 104 | 105 | ```java 106 | Svix svix = new Svix("AUTH_TOKEN"); 107 | 108 | MessageIn message = new MessageIn() 109 | .eventType("invoice.paid") 110 | .eventId("evt_Wqb1k73rXprtTm7Qdlr38G") 111 | .payload("{" + 112 | "\"type\": \"invoice.paid\"," + 113 | "\"id\": \"invoice_WF7WtCLFFtd8ubcTgboSFNql\"," + 114 | "\"status\": \"paid\"," + 115 | "\"attempt\": 2" + 116 | "}"); 117 | 118 | PostOptions opts = new PostOptions() 119 | .idempotencyKey("fd56a56b-838d-4456-8b83-390802672895"); 120 | 121 | svix.getMessage() 122 | .create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", message, opts); 123 | ``` 124 | 125 | 126 | 127 | 128 | ```kotlin 129 | val svix = Svix("AUTH_TOKEN") 130 | svix.message.create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", 131 | MessageIn( 132 | eventType = "invoice.paid", 133 | payload = mapOf( 134 | "type" to "invoice.paid", 135 | "id" to "invoice_WF7WtCLFFtd8ubcTgboSFNql", 136 | "status" to "paid", 137 | "attempt" to 2 138 | ), 139 | eventId = "evt_Wqb1k73rXprtTm7Qdlr38G"), 140 | PostOptions( 141 | idempotencyKey = "fd56a56b-838d-4456-8b83-390802672895")) 142 | ``` 143 | 144 | 145 | 146 | 147 | ```ruby 148 | svix = Svix::Client.new("AUTH_TOKEN") 149 | svix.message.create( 150 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0", 151 | Svix::MessageIn.new({ 152 | "event_type" => "invoice.paid", 153 | "payload" => { 154 | "type": "invoice.paid", 155 | "id" => "invoice_WF7WtCLFFtd8ubcTgboSFNql", 156 | "status" => "paid", 157 | "attempt" => 2 158 | }, 159 | "event_id" => "evt_Wqb1k73rXprtTm7Qdlr38G"}), 160 | { "idempotency_key" => "fd56a56b-838d-4456-8b83-390802672895" }) 161 | ``` 162 | 163 | 164 | 165 | 166 | ```csharp 167 | var svix = new SvixClient("AUTH_TOKEN", new SvixOptions("https://api.us.svix.com")); 168 | var message = new MessageIn( 169 | eventType: "invoice.paid", 170 | payload: new { 171 | type = "invoice.paid", 172 | id = "invoice_WF7WtCLFFtd8ubcTgboSFNql", 173 | status = "paid", 174 | attempt = 2 175 | }, 176 | eventId: "evt_Wqb1k73rXprtTm7Qdlr38G" 177 | ); 178 | await svix.Message.CreateAsync( 179 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0", 180 | message, 181 | null, 182 | "fd56a56b-838d-4456-8b83-390802672895" 183 | ); 184 | ``` 185 | 186 | 187 | 188 | 189 | ``` 190 | Idempotency is not yet supported in the Svix CLI. 191 | ``` 192 | 193 | 194 | 195 | 196 | ```shell 197 | curl "https://api.us.svix.com/api/v1/app/app_Xzx8bQeOB1D1XEYmAJaRGoj0/msg/" \ 198 | -H "Accept: application/json" \ 199 | -H "Content-Type: application/json" \ 200 | -H "Authorization: Bearer AUTH_TOKEN" \ 201 | -H "Idempotency-Key: fd56a56b-838d-4456-8b83-390802672895" 202 | -d '{ "eventType": "invoice.paid", "eventId": "evt_Wqb1k73rXprtTm7Qdlr38G", "payload": { "type": "event.type", "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", "status": "paid", "attempt": 2 } }' 203 | ``` 204 | 205 | 206 | 207 | 208 | Svix's idempotency works by saving the resulting status code and body of the first request made for any given idempotency key for any successful request. Subsequent requests with the same key return the same result for a period of up to 12 hours. 209 | 210 | Please note that idempotency is only supported for `POST` requests. 211 | -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/advanced-endpoints-setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/advanced-endpoints-setting.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/fifo-endpoint-batching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/fifo-endpoint-batching.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/fifo-endpoint-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/fifo-endpoint-create.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/object-storage-endpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/object-storage-endpoints.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/polling-endpoint-create-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/polling-endpoint-create-key.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/polling-endpoint-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/polling-endpoint-create.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/polling-endpoint-details-create-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/polling-endpoint-details-create-key.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/polling-endpoint-setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/polling-endpoint-setting.png -------------------------------------------------------------------------------- /docs/img/advanced-endpoints/s3-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/advanced-endpoints/s3-configuration.png -------------------------------------------------------------------------------- /docs/img/api-access-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/api-access-page.png -------------------------------------------------------------------------------- /docs/img/attempts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/attempts.png -------------------------------------------------------------------------------- /docs/img/channels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/channels.png -------------------------------------------------------------------------------- /docs/img/congratulations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/congratulations.png -------------------------------------------------------------------------------- /docs/img/connectors-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors-endpoint.png -------------------------------------------------------------------------------- /docs/img/connectors-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors-list.png -------------------------------------------------------------------------------- /docs/img/connectors/connect-to-discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/connect-to-discord.png -------------------------------------------------------------------------------- /docs/img/connectors/connect-to-hubspot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/connect-to-hubspot.png -------------------------------------------------------------------------------- /docs/img/connectors/connect-to-slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/connect-to-slack.png -------------------------------------------------------------------------------- /docs/img/connectors/connect-to-teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/connect-to-teams.png -------------------------------------------------------------------------------- /docs/img/connectors/connect-to-windmill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/connect-to-windmill.png -------------------------------------------------------------------------------- /docs/img/connectors/custom-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/custom-integration.png -------------------------------------------------------------------------------- /docs/img/connectors/logos/closecrm-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/customerio-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/discord-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/hubspot-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/inngest-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/connectors/logos/inngest-icon.png -------------------------------------------------------------------------------- /docs/img/connectors/logos/loops-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/microsoft-teams-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/resend-icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/segment-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/sendgrid-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/slack-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/vercel-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/webhook-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/windmill-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /docs/img/connectors/logos/zapier-icon.svg: -------------------------------------------------------------------------------- 1 | zapier-logomark -------------------------------------------------------------------------------- /docs/img/dashboard-preview-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/dashboard-preview-button.png -------------------------------------------------------------------------------- /docs/img/edit-transformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/edit-transformation.png -------------------------------------------------------------------------------- /docs/img/embedded-management-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/embedded-management-ui.png -------------------------------------------------------------------------------- /docs/img/enable-transformations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/enable-transformations.png -------------------------------------------------------------------------------- /docs/img/endpoint-authentication/endpoint-authentication-configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/endpoint-authentication/endpoint-authentication-configure.png -------------------------------------------------------------------------------- /docs/img/endpoint-authentication/endpoint-authentication-enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/endpoint-authentication/endpoint-authentication-enable.png -------------------------------------------------------------------------------- /docs/img/endpoint-authentication/endpoint-configure-mtls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/endpoint-authentication/endpoint-configure-mtls.png -------------------------------------------------------------------------------- /docs/img/endpoint-authentication/endpoint-configure-oauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/endpoint-authentication/endpoint-configure-oauth.png -------------------------------------------------------------------------------- /docs/img/env-specific-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/env-specific-settings.png -------------------------------------------------------------------------------- /docs/img/event-catalog-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/event-catalog-config.png -------------------------------------------------------------------------------- /docs/img/event-catalog-published.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/event-catalog-published.png -------------------------------------------------------------------------------- /docs/img/event-type-openapi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/event-type-openapi.png -------------------------------------------------------------------------------- /docs/img/event-type-selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/event-type-selection.png -------------------------------------------------------------------------------- /docs/img/import-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/import-export.png -------------------------------------------------------------------------------- /docs/img/ngrok-signing-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/ngrok-signing-key.png -------------------------------------------------------------------------------- /docs/img/ngrok-webhook-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/ngrok-webhook-provider.png -------------------------------------------------------------------------------- /docs/img/opentelemetry-spans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/opentelemetry-spans.png -------------------------------------------------------------------------------- /docs/img/schema-add-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/schema-add-code.png -------------------------------------------------------------------------------- /docs/img/schema-add-schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/schema-add-schema.png -------------------------------------------------------------------------------- /docs/img/schema-editor-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/schema-editor-basic.png -------------------------------------------------------------------------------- /docs/img/schema-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/schema-preview.png -------------------------------------------------------------------------------- /docs/img/slack-portal-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/slack-portal-connect.png -------------------------------------------------------------------------------- /docs/img/slack-portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/slack-portal.png -------------------------------------------------------------------------------- /docs/img/svix-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/svix-play.png -------------------------------------------------------------------------------- /docs/img/transformations-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/transformations-card.png -------------------------------------------------------------------------------- /docs/img/tutorials/slack-connectors/advanced-formatting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/tutorials/slack-connectors/advanced-formatting.png -------------------------------------------------------------------------------- /docs/img/tutorials/slack-connectors/new-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/tutorials/slack-connectors/new-endpoint.png -------------------------------------------------------------------------------- /docs/img/tutorials/slack-connectors/slack-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/tutorials/slack-connectors/slack-notification.png -------------------------------------------------------------------------------- /docs/img/tutorials/slack-connectors/testing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/tutorials/slack-connectors/testing.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-1.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-2.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-3.1.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-3.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-4.png -------------------------------------------------------------------------------- /docs/img/zap-creation-step-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zap-creation-step-5.png -------------------------------------------------------------------------------- /docs/img/zapier-integration-generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/docs/img/zapier-integration-generator.png -------------------------------------------------------------------------------- /docs/incoming-webhooks.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Operational Webhooks 3 | --- 4 | 5 | import EventTypeTabs from '@theme/EventTypeTags'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | In addition to enabling you to send webhooks to your customers, Svix also sends you webhooks about the events in your Svix environment. We call these: "Operational webhooks". 9 | 10 | These webhooks let you build powerful workflows and automations over events in your account. For example, a common one is to monitor for [`endpoint.disabled`](https://api.svix.com/docs#tag/Webhook/operation/endpoint.disabled), which triggers when an endpoint has been automatically disabled after multiple days of failing, and notify your customer automatically via email when that happens. 11 | 12 | To subscribe to operational webhooks, please head to the [Operational Webhooks](https://dashboard.svix.com/webhooks) section of the dashboard. 13 | 14 | For more information on the exact events, their schemas, and other related documentation please refer to the [the webhooks section](https://api.svix.com/docs#tag/Webhook) of the API reference documentation. 15 | -------------------------------------------------------------------------------- /docs/integrations/advanced-zapier.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Zapier Integrations 3 | --- 4 | 5 | [Zapier](https://zapier.com/) is an online automation tool that connects apps and services. 6 | You can build a Zapier integration that allows your customers to connect your service to other services with Svix. 7 | 8 | The Svix auto-generated Zapier integration is a Node/JavaScript project meaning it's defined as code and very easy to customize. 9 | 10 | This doc gives guidance on ways to enhance your Zapier webhook integration powered by Svix. 11 | 12 | :::info[Prerequisites] 13 | Before reading this doc, we strongly recommend reading the [Build a Zapier Integration](./zapier) docs. 14 | This doc assumes you're familiar with the basics of Svix Integrations and have a functional API key-based Zapier webhook integration. 15 | ::: 16 | 17 | ## Upgrade an existing integration 18 | 19 | If you add or change your event types, you may want to upgrade your Zapier integration to a new auto-generated integration. 20 | 21 | ### Upgrading to an updated auto-generated package 22 | 23 | Follow the [Download the package](./zapier#download-the-package) section of the [Build a Zapier Integration](./zapier) guide to download an updated auto-generated package. Then follow the steps below to link the new package to the existing integration and deploy. 24 | 25 | #### Build and deploy 26 | 27 | After downloading and extracting the auto-generated integration package, download the package dependencies: 28 | 29 | ``` 30 | npm install 31 | ``` 32 | 33 | Then, link the new integration package to your existing Zapier integration using the Zapier CLI [link command](https://platform.zapier.com/cli_docs/cli#link): 34 | ``` 35 | zapier link 36 | ``` 37 | 38 | Finally, build & deploy the integration to Zapier through the [push command](https://platform.zapier.com/cli_docs/cli#push): 39 | ``` 40 | zapier push 41 | ``` 42 | 43 | ### Managing integration versions 44 | 45 | If you wish to version bump your integration, be sure to update your integration's `package.json` (shown below) to reflect the new version number. It defaults to `1.0.0`. 46 | 47 | ```json 48 | { 49 | "name": "zapier-webhook-integration-with-svix", 50 | "version": "1.0.0", // <------ CHANGE ME 51 | "main": "index.js", 52 | // << truncated >> 53 | } 54 | ``` 55 | 56 | Zapier supports complex versioning use cases including migrating users during a version bump. Review the [Zapier Platform](https://platform.zapier.com/cli_docs/docs#deploying-an-app-version) docs for more information. 57 | 58 | ### Upgrading a customized integration 59 | 60 | If you have customized your integration package, you will need to manually reconcile the diff. 61 | We strongly recommend using version control like git to track the changes and aid in merging. 62 | 63 | ## Alternative authentication schemes 64 | 65 | By default, the auto-generated Zapier integration uses [custom (API key)](https://platform.zapier.com/cli_docs/docs#custom) authentication type and requires users to explicitly provide an application ID and integration key. 66 | The default scheme might be undesirable because it requires the user to copy-paste credentials and doesn't connect to your existing auth flows. 67 | 68 | To provide a better user experience that hides the Svix constructs, we recommend using [OAuth2](https://platform.zapier.com/cli_docs/docs#oauth2) or [Session](https://platform.zapier.com/cli_docs/docs#session) authentication. See the [Zapier Platform authentication](https://platform.zapier.com/cli_docs/docs#authentication) docs for more information. 69 | - With OAuth2, Zapier will redirect your user to your site where you can authenticate them and send Zapier back an access token. An demonstration of this flow's user experience is shown in the video below. 70 | - With Session Auth, Zapier will show a login form for your user to provide their username and password. Those are securely sent to your service where you'll send Zapier back a session token. 71 | 72 | With either authentication flow, you can make another request to your APIs to exchange the access/session token for the Svix application ID and integration key. 73 | An example of that API endpoint for a Flask-based service might look like the following: 74 | 75 | ```py 76 | svix = Svix("AUTH_TOKEN") 77 | 78 | @app.route('/webhook/integration/') 79 | @auth_required 80 | def get_svix_integration_key(): 81 | app_id = ... # get your user's app id 82 | integration_out = svix.integration.create(app_id, IntegrationIn( 83 | name="Zapier Integration" 84 | )) 85 | ... # store your user's zapier integration ID 86 | key_out = svix.integration.get_key(app_id, integration_out.id) 87 | return jsonify({ 88 | "application_id": app_id, 89 | "integration_key": key_out.key 90 | }) 91 | ``` 92 | 93 | #### OAuth2 Flow User Experience 94 | 95 |
96 | 98 |
99 | 100 | ## Custom triggers 101 | 102 | ### Multiple event types per trigger 103 | 104 | If you wish to configure triggers with multiple event types, you can modify the `filterTypes` field on the create endpoint operation during the trigger's subscribe hook. 105 | 106 | If you wish your users to specify a set of event types when creating the trigger, you can use Zapier's [input fields](https://platform.zapier.com/cli_docs/docs#input-fields) feature. 107 | 108 | ### Additional trigger fields 109 | 110 | By default, the auto-generated integration contains the example payload that you provided with the event type schema. Zapier supports explicitly defining the list of output fields for the trigger. If you have optional fields or multiple event types with different schemas, we recommend explicitly defining the [output fields](https://platform.zapier.com/cli_docs/docs#output-fields) on the trigger. 111 | 112 | ### Adding custom actions or triggers 113 | 114 | The auto-generated Zapier integration can be extended with additional actions, triggers, creates, or other Zapier resources. More information on this is available on the [Zapier CLI platform docs](https://platform.zapier.com/cli_docs/docs). 115 | 116 | The Zapier CLI can modify your package (e.g. creating a new action from a template). More information on that is available on the [Zapier CLI Reference](https://platform.zapier.com/cli_docs/cli). 117 | -------------------------------------------------------------------------------- /docs/integrations/ngrok.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using the ngrok Integration 3 | --- 4 | 5 | [ngrok](https://ngrok.com) is a staple tool for many developers that creates tunnels between networks. It is often used to expose a port on localhost to the public Internet, but with ngrok Cloud Edge it can be also be used to secure traffic from the Internet to production cloud environments. 6 | 7 | In this tutorial, we will learn how to verify Svix webhook requests using ngrok both for local development and on ngrok Cloud Edge. 8 | 9 | :::info 10 | This tutorial assumes you are already familiar with the [Svix webhooks service](https://www.svix.com) and [ngrok](https://ngrok.com). If this is your first time using Svix, 11 | we recommend you first check out our [quickstart documentation](https://docs.svix.com/overview). We also recommend checking out ngrok's documentation on [Webhook Verification](https://ngrok.com/docs/http/webhook-verification/). 12 | ::: 13 | 14 | ## Verify Svix webhooks locally with ngrok CLI 15 | 16 | ### Install ngrok 17 | If you haven't already, [install the ngrok CLI](https://ngrok.com/download). 18 | 19 | ### Create a tunnel 20 | 21 | First, create an endpoint in the Consumer App Portal, and copy the endpoint's Signing Signature. Then, run the following ngrok command on your computer's terminal, replacing SIGNATURE with the Signing Signature you just copied: 22 | 23 | ```sh 24 | ngrok http 3000 --verify-webhook=svix --verify-webhook-secret=SIGNATURE 25 | ``` 26 | 27 | When you run the command, ngrok should generate URL that looks like `https://d7f4c8296c55.ngrok.io`. Copy that URL, and set it as the URL for the Svix endpoint you previously created in the Consumer App Portal. 28 | 29 | Assuming you are running a service on port 3000, all Svix webhooks to your endpoint will now be forwarded to that local service by ngrok. And because you configured the `--verify-webhook` and `--verify-webhook-secret` options, ngrok will only forward verified Svix webhooks. 30 | 31 | ## Verify Svix Webhooks on ngrok Cloud Edge 32 | 33 | ### Create a Svix endpoint 34 | 35 | Create an endpoint in the consumer application portal. You'll need the newly-created endpoint's Signing Signature later on. 36 | 37 | ### Sign up for ngrok 38 | 39 | Create an account on [ngrok.com](https://ngrok.com). 40 | 41 | ### Create an ngrok Edge 42 | 43 | Login to the [ngrok dashboard](https://dashboard.ngrok.com). Using the menu on the left, expand "Cloud Edge" and choose "Edges." Create an edge by clicking the "New edge" button and choose "HTTPS Edge". 44 | 45 | ### Configure ngrok Edge 46 | 47 | On the Edge configuration page, find and click the "Webhooks Verification" menu item, and click "Begin Setup." Choose Svix as your webhook provider: 48 | 49 | ![Selecting Svix as the Webhook Provider](../img/ngrok-webhook-provider.png) 50 | 51 | And paste your endpoint's Signing Signature from Svix as the webhook signing key: 52 | 53 | ![Entering your Svix endpoint's Signing Key](../img/ngrok-signing-key.png) 54 | -------------------------------------------------------------------------------- /docs/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | slug: / 4 | --- 5 | 6 | Svix makes sending webhooks easy and reliable by offering [webhook sending as a service](https://www.svix.com). With Svix you can start sending webhooks in minutes, while ensuring robust deliverability, and a great developer experience for your users. 7 | 8 | ## Webhooks are harder than they seem. 9 | 10 | Webhooks require a lot more engineering time, resources, and ongoing maintenance than you would first expect. 11 | 12 | When building your own webhooks you have to deal with a lot of challenges, such as: unreliable user endpoints, which fail or hang more often than you think; monitoring and reliability of your webhook system; security implications which are unique to webhooks and much more. 13 | 14 | This is where we come in. With Svix you can start sending webhooks in under five minutes, and we take care of all of the above and more. 15 | -------------------------------------------------------------------------------- /docs/onboarding.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Onboarding 3 | --- 4 | 5 | The onboarding document has now been merged with the rest of the documentation. In order to get started just navigate to the [main docs section](./introduction.mdx). 6 | -------------------------------------------------------------------------------- /docs/opentelemetry-streaming.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: OpenTelemetry Streaming 3 | --- 4 | 5 | :::info[Important] 6 | OpenTelemetry streaming is only available as part of the Enterprise tier. Please refer to [the pricing page](https://www.svix.com/pricing/) for more information. 7 | Some of this functionality can also be achieved by using [Operational Webhooks](/incoming-webhooks). 8 | ::: 9 | 10 | Svix offers OpenTelemetry streaming as a way to stream webhook delivery traces to observability platforms that support OpenTelemetry such as Datadog, Grafana, Coralogix, and most other observability platforms. 11 | 12 | ## What does Svix send 13 | 14 | Svix sends OpenTelemetry traces as two spans: the outer span `message_attempt` and the inner span `http_attempt`. 15 | 16 | Both spans include the following attributes: 17 | ```rust 18 | start: DateTime // Included as the span start time (not a field) 19 | end: DateTime // Included as the span end time (not a field) 20 | org_id: OrganizationId 21 | app_id: ApplicationId 22 | app_uid: Option 23 | endpoint_id: EndpointId 24 | msg_id: MessageId 25 | msg_event_id: Option 26 | event_type: EventTypeName, 27 | attempt_count: u16 28 | status: MessageStatus 29 | ``` 30 | 31 | And the inner `http_attempt` also has: 32 | ```rust 33 | http.response.status_code: i16 34 | ``` 35 | 36 | Here is an example of how it looks like in an observability dashboard: 37 | 38 | ![OpenTelemetry Spans](./img/opentelemetry-spans.png) 39 | 40 | ## How to use it 41 | 42 | The raw spans sent by Svix can be used in a variety of ways: 43 | * Deriving graphs and metrics for your observability dashboards. 44 | * Alerting for when specific customers fail over a certain threshold or their latency increases. 45 | * Storing raw delivery logs for compliance reasons. 46 | * Much more... 47 | 48 | For specific instructions on how to achieve the above, please refer to the documentation by your observability provider or [contact Svix support](https://www.svix.com/contact/). 49 | -------------------------------------------------------------------------------- /docs/overview.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Entities Overview 3 | --- 4 | 5 | This section provides an overview of the common Svix entities. 6 | 7 | The Svix entities are mostly hierarchical with the environment being the main isolation unit, below it there are applications which represent a target for messages, and even below there are endpoints which represent a destination to send webhooks to. 8 | 9 | ![Entities diagram](img/entity-graph.svg) 10 | 11 | 12 | ## IDs and UIDs 13 | 14 | All Svix entities have IDs as their unique identifiers. Some entities, such as applications and endpoints, support an additional unique identifier called `UID`. 15 | 16 | These `UID`s can be used interchangeably with `ID`s all throughout the API. So for example, you can send a message to a specific `ID`, or a specific `UID`, both will work. 17 | 18 | This enables you to use Svix in a completely stateless manner, without having to store the Svix identifiers (or anything) in your own database. 19 | 20 | 21 | 22 | ## Consumer Applications 23 | 24 | When sending messages using the Svix API you will be sending them to a specific application (consumer), which will then distribute them to the associated endpoints. 25 | In most cases you would want to create one application for each of our customers, though in some cases you may want to create multiple applications per customer. 26 | 27 | Each application lies within its own security context. Each application is completely isolated from another, but also there is no isolation within an application. This means that you should assume that every message sent to an application can be viewed by all of the endpoints subscribed to that application. What does this mean in practice? Create different applications for different security contexts. 28 | 29 | You can define a `uid` for an application which can then be used interchangeably in the API with the application's `id`. Most people set the `uid` to their own internal customer id, so for example if they have a user called `some-user` they would set the `uid` to `some-user` and then use it with the Svix API as follows: 30 | 31 | ```javascript 32 | svix.application.update('some-user', ApplicationIn(/* ... */)) 33 | ``` 34 | 35 | It's recommended to create an application (using the API) for each of their customers when they sign up to your service. You can however also create it "lazily", and only do it when they enable webhooks. 36 | 37 | Alternatively, you can create an application the first time you send a message to it. When sending a message you can, optionally, send an `application` property along with it. If the application does not exist yet it will be created; if it does then the `application` property will be ignored. 38 | 39 | ```javascript 40 | svix.message.create('some-user', { 41 | application: ApplicationIn(/* ... */), 42 | payload: {/* ... */}, 43 | }) 44 | ``` 45 | 46 | 47 | ## Endpoints 48 | 49 | Endpoints represent a target for messages sent to a specific application. You can have multiple endpoints per application, and every message sent to the application will be sent to all of them. Endpoints can have filters applied to them which will prevent them from receiving specific messages. The most common such filter is [event type](event-types.mdx), where an endpoint can choose to only subscribe to a limited set of events. 50 | 51 | Endpoints can be created by you using [the create endpoint API](https://api.svix.com/docs#operation/create_endpoint_api_v1_app__app_id__endpoint__post), though they are most commonly created by your customers using the [application portal](app-portal.mdx) or a similar UI. 52 | 53 | Like with applications, you can define a `uid` for an endpoint which can then be used interchangeably in the API with the endpoint's `id`. 54 | 55 | 56 | ## Messages 57 | 58 | Messages are the webhooks being sent. They can have a content, event type, and a few other properties. 59 | 60 | A message sent to an application will be sent to all of its endpoints (based on aforementioned filtering rules). 61 | When an application has no endpoints, or when no endpoints match a message, the sent message is just saved to the database but is not actually sent to a customer. 62 | 63 | If a message delivery fails, it will be attempted multiple times until either it succeeds, or its attempts have been exhausted. Please refer to the [retry schedule](retries.mdx) for more information. 64 | 65 | Messages can have an associated `eventId`. The `eventId` is used to map a message from Svix to one in your system so you can easily map a message to the reason why you sent it. 66 | 67 | 68 | ## Attempts 69 | 70 | Attempts represent an attempt that has been made to send a message to an endpoint. Attempts also record the response content of the attempt, the response HTTP status code, as well as other properties. Each attempt to send a message is recorded in an attempt entity which can then be queried (there can be multiple when there are failures). 71 | 72 | 73 | ## Event Types 74 | 75 | Event types are identifiers on messages that describe the message being sent and implies its associated schema. Each message has exactly one event type, and endpoints can listen to all, or a subset of the created event types. 76 | 77 | Event types schemas are used for things like "send example webhook" in the application portal, the event catalog, and the [automatic Zapier integration](integrations/zapier.mdx). 78 | 79 | You will need to create event types for them to be shown in the application portal and used by endpoints. 80 | 81 | 82 | ## Environments 83 | 84 | Environments are completely isolated Svix environments that have their own API keys, data, and settings. You can think of Svix environments are completely separate accounts. 85 | 86 | You can create as many environments as you want in the dashboard UI depending on your needs. Most people create one environment for `Production`, one for `Staging` and one for `Development`. 87 | Some people create multiple `Production` environments based on geographical regions e.g. `Production EU` and `Production US`. 88 | -------------------------------------------------------------------------------- /docs/play.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Svix Play - webhook debugger 3 | --- 4 | 5 | You can use the [Svix Play webhook playground and debugger](https://www.svix.com/play/) to easily inspect, test and debug operational webhooks. 6 | 7 | It's a very useful tool for anyone developing webhooks - for both senders and consumers. More so, it's available to everyone for free, not just users of Svix. No signup required! 8 | 9 | ## How to use 10 | 11 | To use Svix Play, just go to [play.svix.com](https://play.svix.com) 12 | 13 | You'll be redirected to the main Svix Play page, where you can copy your unique webhook URL and start sending it requests. Every request sent to this URL will respond with a successful `200 OK` response, and will be visible in the UI for you to inspect. 14 | 15 | This is how it looks like: 16 | 17 | ![Svix Play](./img/svix-play.png) 18 | 19 | ## Relay mode (Svix CLI) 20 | 21 | In relay mode, you can use the Svix CLI for a free and secure public URL that relays requests to your local development server. All of these requests are then viewable in the [Svix Play webhook debugger](https://www.svix.com/play/) UI. 22 | 23 | All you have to do is install the [Svix CLI](https://github.com/svix/svix-cli) and run `svix listen` as shown here: 24 | 25 | ``` 26 | $ svix listen http://localhost:8080/webhook/ 27 | 28 | Webhook relay is now listening at 29 | https://play.svix.com/in/e_94XdF-OwN3EaTKty4izJDWRAH3V/ 30 | 31 | All requests on this endpoint will be forwarded to your local URL: 32 | http://localhost:8080/webhook/ 33 | ``` 34 | 35 | ## Advanced usage 36 | 37 | ### Custom response codes 38 | 39 | Under normal usage, Svix Play (in echo mode) automatically returns successful responses to every request with the HTTP response code `200 OK`. 40 | 41 | However, in some cases you may want to check how your webhook system responds to failures. For example, does auto-retry work? Are errors properly handled? 42 | 43 | To enable that, Svix Play supports returning custom error codes by using the `force_status_code` query parameter when using our public API. 44 | 45 | For example, if your URL was: 46 | 47 | ``` 48 | https://api.play.svix.com/api/v1/in/e_94XdF-OwN3EaTKty4izJDWRAH3V/ 49 | ``` 50 | 51 | You can change it to the following URL in order to make it always return `404 Not Found`: 52 | 53 | ``` 54 | https://api.play.svix.com/api/v1/in/e_94XdF-OwN3EaTKty4izJDWRAH3V/?force_status_code=404 55 | ``` 56 | 57 | ### Echo Request Bodies 58 | 59 | Under normal usage, Svix Play will return an empty body to every request towards an `/in/` route. 60 | 61 | If you wish to have the request body echoed back instead, set the `echo_body` query parameter to `true` when using our public API. 62 | 63 | For example, if your URL was: 64 | 65 | ``` 66 | https://api.play.svix.com/api/v1/in/e_94XdF-OwN3EaTKty4izJDWRAH3V/ 67 | ``` 68 | 69 | You can change it to the following URL to make it respond with the same body it was given. 70 | 71 | ``` 72 | https://api.play.svix.com/api/v1/in/e_94XdF-OwN3EaTKty4izJDWRAH3V/?echo_body=true 73 | ``` 74 | 75 | 76 | ### Programmatic Use of the Public API 77 | 78 | To automatically test webhooks in your test suite, it is possible to use the public API to record dispatched webhooks and to see the history for a given token. 79 | 80 | You will need an echo token to start. These tokens are freely generated and are simply used to distinguish active sessions. These tokens are the same as you would see when visiting the Svix Play [web application](https://play.svix.com). Such a token may be easily generated programmatically with a POST request to the following route: 81 | 82 | ``` 83 | https://api.play.svix.com/api/v1/token/generate/ 84 | ``` 85 | 86 | Next, send any number of events to the following route with any HTTP method: 87 | 88 | ``` 89 | https://api.play.svix.com/api/v1/in/{your token here}/ 90 | ``` 91 | 92 | This route can take the query parameters `force_status_code`, `echo_body`, and `endpoint_signing_key`. 93 | 94 | Next, you'll want to analyze the record of events for this token using the `/history/` route as follows: 95 | 96 | ``` 97 | https://api.play.svix.com/api/v1/history/{your token here}/ 98 | ``` 99 | 100 | The output of this route will look something like this: 101 | 102 | ``` 103 | { 104 | "iterator": "2Nmzzn6O30LTlFwegZYjrIEuRPL", 105 | "data": [ 106 | { 107 | "id": "2Nmzzn6O30LTlFwegZYjrIEuRPL", 108 | "url": "/api/v1/in/e_DCFOA2693TG8wtcRLDaD8aFOm3J/", 109 | "method": "GET", 110 | "created_at": "2023-03-31T17:38:29.921531707Z", 111 | "body": "", 112 | "headers": { 113 | "accept": "*/*", 114 | "user-agent": "curl/7.81.0" 115 | }, 116 | "response": { 117 | "status_code": 204, 118 | "headers": {}, 119 | "body": "" 120 | }, 121 | "ip": null 122 | } 123 | ] 124 | } 125 | ``` 126 | 127 | 128 | This route allows one query parameter, `iterator`. Given the `id` of one of the events sent, the history route will only return events that were received after that ID. 129 | 130 | For ease of use, the JSON object returned by the `/history/` route includes an `iterator` field which is set to the ID of the last received request, or the same iterator as given if no additional requests since then have been recorded. 131 | 132 | For example, if your URL was: 133 | 134 | ``` 135 | https://api.play.svix.com/api/v1/history/e_94XdF-OwN3EaTKty4izJDWRAH3V/ 136 | ``` 137 | 138 | You can change it to the following URL in order to only display events that were received after the request with ID `2Nvo8F66yxe0cT7lrVmsbUg97He`: 139 | 140 | ``` 141 | https://api.play.svix.com/api/v1/history/e_94XdF-OwN3EaTKty4izJDWRAH3V/?iterator=2Nvo8F66yxe0cT7lrVmsbUg97He 142 | ``` 143 | 144 | -------------------------------------------------------------------------------- /docs/rate-limit.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Rate Limiting Endpoints 3 | --- 4 | 5 | import CodeTabs from '@theme/CodeTabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | 9 | While Svix can handle however many messages you send us, your customers' endpoints may not be able to. This is why the Svix API includes rate-limiting (throttling) calls to customer endpoints. 10 | 11 | This lets you send as many webhooks per second as you want without having to worry about overloading your customers' systems. 12 | 13 | 14 | ## How does it work? 15 | 16 | You can set a rate limit on both the application, which applies to all of the endpoints for that application, or the endpoint to only set it for that specific endpoint. 17 | 18 | The rate limit is defined as a limit for the number of messages per second to send to the endpoint. After the limit is reached, requests will get throttled so to keep a consistent rate under the limit. 19 | 20 | Due to the nature of distributed systems the actual rate of messages can sometimes be slightly above the enforce rate limit. So for example, if you set a rate limit of 1,000 per seconds, an endpoint may potentially get messages at a rate of 1,050 or even higher. 21 | 22 | 23 | ## Setting the limits 24 | 25 | You can set the rate limit on either the application or each of its endpoint. The rate limit is enforced per endpoint, so limits set on the application propagate to the endpoints and can be overridden by the endpoint specific settings. 26 | 27 | ### Setting application rate limits 28 | 29 | You can set the rate limit on an application when [creating](https://api.svix.com/docs#operation/create_application_api_v1_app__post) or [updating](https://api.svix.com/docs#operation/update_application_api_v1_app__app_id___put) it using the `rateLimit` property. The value is the number of messages per second. 30 | 31 | 32 | 33 | 34 | ```js 35 | import { Svix } from "svix"; 36 | 37 | const svix = new Svix("AUTH_TOKEN"); 38 | const app = await svix.application.create({name: "Application name", rateLimit: 1000}); 39 | ``` 40 | 41 | 42 | 43 | 44 | ```python 45 | from svix.api import Svix, ApplicationIn 46 | 47 | svix = Svix("AUTH_TOKEN") 48 | app = svix.application.create(ApplicationIn(name="Application name", rate_limit=1000)) 49 | ``` 50 | 51 | 52 | 53 | 54 | ```go 55 | import ( 56 | svix "github.com/svix/svix-webhooks/go" 57 | ) 58 | 59 | svixClient := svix.New("AUTH_TOKEN", nil) 60 | app, err := svixClient.Application.Create(ctx, &svix.ApplicationIn{ 61 | Name: "Application name", 62 | RateLimit: 1000, 63 | })} 64 | ``` 65 | 66 | 67 | 68 | 69 | ```rust 70 | use svix::api::{ApplicationIn, Svix, SvixOptions}; 71 | 72 | let svix = Svix::new("AUTH_TOKEN".to_string(), None); 73 | let app = svix 74 | .application() 75 | .create( 76 | ApplicationIn { 77 | name: "Application name".to_string(), 78 | rate_limit: Some(1000), 79 | ..ApplicationIn::default() 80 | }, 81 | None, 82 | ) 83 | .await?; 84 | ``` 85 | 86 | 87 | 88 | 89 | ```java 90 | import com.svix.models.ApplicationIn; 91 | import com.svix.models.ApplicationOut; 92 | import com.svix.Svix; 93 | 94 | Svix svix = new Svix("AUTH_TOKEN"); 95 | ApplicationOut app = svix.getApplication().create(new ApplicationIn().name("Application name").rateLimit(1000)); 96 | ``` 97 | 98 | 99 | 100 | 101 | ```kotlin 102 | import com.svix.kotlin.models.ApplicationIn; 103 | import com.svix.kotlin.Svix; 104 | 105 | val svix = Svix("AUTH_TOKEN") 106 | val applicationOut = svix.application.create(ApplicationIn( 107 | name = "Application name", 108 | rateLimit = 1000)) 109 | ``` 110 | 111 | 112 | 113 | 114 | ```ruby 115 | require "svix" 116 | 117 | svix = Svix::Client.new("AUTH_TOKEN") 118 | 119 | application_out = svix.application.create(Svix::ApplicationIn.new({ 120 | "name" => "Application name" 121 | "rate_limit" => 1000})) 122 | ``` 123 | 124 | 125 | 126 | 127 | ```csharp 128 | using Svix; 129 | using Svix.Model; 130 | using Svix.Models; 131 | 132 | var svix = new SvixClient("AUTH_TOKEN", new SvixOptions("https://api.us.svix.com")); 133 | await svix.Application.CreateAsync(new EventTypeIn( 134 | name: "Application Name", 135 | rateLimit: 1000 136 | )) 137 | ``` 138 | 139 | 140 | 141 | 142 | ```shell 143 | export SVIX_AUTH_TOKEN='AUTH_TOKEN' 144 | svix application create '{ "name": "Application name", "rateLimit": 1000 }' 145 | ``` 146 | 147 | 148 | 149 | 150 | ```shell 151 | curl -X POST "https://api.us.svix.com/api/v1/app/" \ 152 | -H "Accept: application/json" \ 153 | -H "Content-Type: application/json" \ 154 | -H "Authorization: Bearer AUTH_TOKEN" \ 155 | -d '{"name": "Application name", "rateLimit": 1000}' 156 | ``` 157 | 158 | 159 | 160 | 161 | 162 | ### Setting endpoint rate limits 163 | 164 | Similar to the application rate limiting, you can set the rate limit on an endpoint when [creating](https://api.svix.com/docs#operation/create_endpoint_api_v1_app__app_id__endpoint__post) or [updating](https://api.svix.com/docs#operation/update_endpoint_api_v1_app__app_id__endpoint__endpoint_id___put) it using the `rateLimit` property. The value is the number of messages per second. 165 | 166 | 167 | ## Intended use 168 | 169 | The rate limit is intended to be used as a sort of a fuse to protect your customers' servers from sudden peaks in message traffic that they may not be able to withstand. With the rate limiting, this peak is smoothened and spread over multiple seconds, sending messages at a rate your customers can handle. 170 | 171 | One important thing to remember with rate limiting, is that if you are limiting to 1,000 messages per second and are consistently sending messages over that limit (e.g. 2,000 per second), the queue will get congested and you will be sending messages with infinitely increasing delays. We can alert you of such issues, and help you address them when they occur, though this is still something to be mindful of. 172 | -------------------------------------------------------------------------------- /docs/receiving/additional-authentication.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Additional Authentication 3 | --- 4 | 5 | Svix [signs all webhooks](./verifying-payloads/why.mdx) in order to ensure the security and authenticity of all of the webhooks being sent. 6 | 7 | This security mechanism is already sufficient (and better) than other methods such as [HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication), and using an authentication token. However, some systems and IT departments have varying requirements for any HTTP request hitting their services (including webhooks), so Svix has built in support for these additional authentication modes. 8 | 9 | ## HTTP Basic Authentication 10 | 11 | HTTP Basic Authentication (Basic Auth), is a common way of sending a server a pair of username and password, or more often a username and auth token. While there are different ways of passing these credentials, the simplest and most common way is by including it as part of the URL. 12 | 13 | You can add it to the URL by prefixing the URL with the username and password (or token) and the @ symbol as such: 14 | 15 | ``` 16 | https://USERNAME:PASSWORD@example.com/webhook/ 17 | ``` 18 | 19 | ## Header based authentication 20 | 21 | Some services require specific headers to be passed in order to be processed by their load balancers or application servers. These services often require a specific authentication token passed in the `Authorization` header, but sometimes there could also be different headers and values. 22 | 23 | Svix supports setting custom headers to be sent for each endpoint both [using the API](https://api.svix.com/docs#operation/update_endpoint_headers_api_v1_app__app_id__endpoint__endpoint_id__headers__put) and using [the application portal](./using-app-portal/adding-endpoints.mdx). 24 | 25 | ## Firewalls (IP blocking) 26 | 27 | Many, often larger, organizations have strict firewall rules for which IPs are allowed to send traffic to their systems. While this is not a very strong security mechanism on its own, it's often useful when used in conjunction with other methods (such as webhook signatures). 28 | 29 | In order to support these organizations and their requirements, Svix only sends webhooks requests from a specific set of IPs as detailed in the [source IP addresses section](./source-ips.mdx). 30 | -------------------------------------------------------------------------------- /docs/receiving/introduction.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | Read to following sections to learn why and how to verify webhooks sent by the Svix service and its customers. 6 | 7 | :::tip 8 | You can use the [Svix Play webhook debugger](https://www.svix.com/play/) and the [Svix CLI](https://github.com/svix/svix-cli) to inspect, test and debug your webhooks during development. 9 | ::: 10 | 11 | ## Webhooks Consumption 101 12 | 13 | Webhooks are how services notify each other of events. At their core they are just a `POST` request to a pre-determined endpoint. The endpoint can be whatever you want, and you can just [add them from the UI](/receiving/using-app-portal/adding-endpoints). You normally use one endpoint per service, and that endpoint listens to all of the event types. For example, if you receive webhooks from Acme Inc., you can structure your URL like: `https://www.example.com/acme/webhooks/`. 14 | 15 | The way to indicate that a webhook has been processed is by returning a `2xx` (status code `200-299`) response to the webhook message within a reasonable time-frame (15s with Svix). It's also important to disable `CSRF` protection for this endpoint if the framework you use enables them by default. 16 | 17 | Another important aspect of handling webhooks is to verify the signature and timestamp when processing them. You can learn more about it in the [why verify section](/receiving/verifying-payloads/why). 18 | -------------------------------------------------------------------------------- /docs/receiving/receiving-with-ingest.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Receiving Webhooks with Ingest 3 | --- 4 | 5 | import CodeTabs from '@theme/CodeTabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | Receiving webhooks with [Svix Ingest] starts by creating a `Source`. 9 | 10 | A `Source` generates an endpoint you can share with a webhook provider as a 11 | destination for their webhooks. 12 | 13 | Ingest supports signature verification schemes and flows used by a variety of 14 | webhook providers. Supported values for a `Source`'s `type` include: 15 | 16 | - `beehiiv` 17 | - `brex` 18 | - `clerk` 19 | - `github` 20 | - `guesty` 21 | - `hubspot` 22 | - `incidentIo` 23 | - `lithic` 24 | - `nash` 25 | - `pleo` 26 | - `replicate` 27 | - `resend` 28 | - `safebase` 29 | - `sardine` 30 | - `segment` 31 | - `shopify` 32 | - `slack` 33 | - `stripe` 34 | - `stych` 35 | - `svix` (that's us!) 36 | - `zoom` 37 | - `adobeSign` 38 | - `docusign` 39 | 40 | Additionally there's the option to configure a `Source` as `genericWebhook` to 41 | skip performing signature verification. This is useful for providers that have 42 | no verification scheme and for providers whose verification scheme is not yet 43 | supported by Ingest. 44 | 45 | :::tip 46 | Let us know if your webhook provider isn't yet supported so we can add it! 47 | ::: 48 | 49 | 50 | ## Create a Source 51 | 52 | Creating a `Source` from the [Ingest Dashboard]: 53 | 54 | ![screenshot of the Ingest Dashboard showing the overview tab for a Source named "demo"](../../static/img/ingest/source-edit.png) 55 | 56 | 57 | 58 | 59 | ```javascript 60 | const svix = new Svix("AUTH_TOKEN"); 61 | const ingestSourceOut = await svix.ingest.source.create({ 62 | name: "myGithubWebhook", 63 | uid: "unique-identifier", 64 | type: "github", 65 | config: { 66 | secret: "SECRET" 67 | }, 68 | }); 69 | ``` 70 | 71 | 72 | 73 | 74 | ```python 75 | svix = Svix("AUTH_TOKEN") 76 | ingest_source_out = svix.ingest.source.create(IngestSourceIn( 77 | name="myGithubWebhook", 78 | uid="unique-identifier", 79 | type="github", 80 | config=GithubConfig( 81 | secret="SECRET" 82 | ), 83 | )) 84 | ``` 85 | 86 | 87 | 88 | 89 | ```go 90 | svixClient := svix.New("AUTH_TOKEN", nil) 91 | ingestSourceOut, err := svixClient.Ingest.Source.Create( 92 | ctx, 93 | &IngestSourceIn{ 94 | Name: "myGithubWebhook", 95 | Uid: "unique-identifier", 96 | Type: IngestSourceInTypeGithub, 97 | Config: GithubConfig{ 98 | Secret: "SECRET", 99 | }, 100 | }, 101 | ) 102 | ``` 103 | 104 | 105 | 106 | 107 | ```kotlin 108 | val svix = Svix("AUTH_TOKEN") 109 | val ingestSourceOut = svix.ingest.source.create(IngestSourceIn( 110 | name = "myGithubWebhook", 111 | uid = "unique-identifier", 112 | config = IngestSourceInConfig.Github(GithubConfig( 113 | secret = "SECRET" 114 | )), 115 | )) 116 | ``` 117 | 118 | 119 | 120 | 121 | ```java 122 | Svix svix = new Svix("AUTH_TOKEN"); 123 | IngestSourceOut ingestSourceOut = svix 124 | .getIngest() 125 | .getSource() 126 | .create(new IngestSourceIn() 127 | .name("myGithubWebhook") 128 | .uid("unique-identifier") 129 | .config(new IngestSourceInConfig.Github(new GithubConfig() 130 | .secret("SECRET") 131 | )) 132 | ); 133 | ``` 134 | 135 | 136 | 137 | 138 | ```ruby 139 | svix = Svix::Client.new("AUTH_TOKEN") 140 | ingest_source_out = svix.ingest.source.create(Svix::IngestSourceIn.new({ 141 | "name": "myGithubWebhook", 142 | "uid": "unique-identifier", 143 | "config": Svix::IngestSourceInConfig::Github.new({ 144 | "secret": "SECRET" 145 | }) 146 | })) 147 | ``` 148 | 149 | 150 | 151 | 152 | ```rust 153 | let svix = Svix::new("AUTH_TOKEN".to_string(), None); 154 | let ingest_source_out = svix.ingest().source().create( 155 | IngestSourceIn { 156 | name: "myGithubWebhook".to_owned(), 157 | uid: Some("unique-identifier".to_owned()), 158 | config: IngestSourceInConfig::Github(GithubConfig { 159 | secret: "SECRET".to_owned(), 160 | ..Default::default() 161 | }), 162 | ..Default::default() 163 | }, 164 | None, 165 | ).await?; 166 | ``` 167 | 168 | 169 | 170 | 171 | ```csharp 172 | var svix = new SvixClient("AUTH_TOKEN", new SvixOptions("https://api.us.svix.com")); 173 | var ingestSourceOut = await svix.Ingest.Source.CreateAsync( 174 | new IngestSourceIn{ 175 | Name = "myGithubWebhook", 176 | Uid = "unique-identifier", 177 | Config = IngestSourceInConfig.Github(new GithubConfig { 178 | Secret = "SECRET", 179 | }), 180 | } 181 | ); 182 | ``` 183 | 184 | 185 | 186 | 187 | ```shell 188 | svix ingest source create '{ 189 | "name": "myGithubWebhook", 190 | "uid": "unique-identifier", 191 | "type": "github", 192 | "config": { 193 | "secret": "SECRET" 194 | } 195 | }' 196 | ``` 197 | 198 | 199 | 200 | 201 | ```shell 202 | curl -X 'POST' \ 203 | 'https://api.eu.svix.com/ingest/api/v1/source' \ 204 | -H 'Authorization: Bearer AUTH_TOKEN' \ 205 | -H 'Accept: application/json' \ 206 | -H 'Content-Type: application/json' \ 207 | -d '{ 208 | "name": "myGithubWebhook", 209 | "uid": "unique-identifier", 210 | "type": "github", 211 | "config": { 212 | "secret": "SECRET" 213 | } 214 | }' 215 | ``` 216 | 217 | 218 | 219 | 220 | ## Tell your provider where to send webhooks 221 | 222 | The Ingest URL is also listed on the [Ingest Dashboard] for each `Source`. 223 | 224 | The `IngestSourceOut` response from the API will include an `ingestUrl` which is 225 | the endpoint you give to your provider, telling them where to send their 226 | webhooks. 227 | 228 | For GitHub, as is used in this example, this is referred to as the 229 | _"Payload URL"_. 230 | 231 | 232 | ## Managing incoming messages 233 | 234 | In the [Ingest Dashboard], the Destinations tab for your `Source` is where you 235 | can configure endpoints, view logs, inspect message payloads, etc. 236 | 237 | ![screenshot of the Ingest Dashboard showing the Destination/Endpoints tab for a Source named "demo"](../../static/img/ingest/destination-endpoints.png) 238 | 239 | Configuring endpoints allows you to forward messages received by Ingest over to 240 | the endpoints you choose using Svix Core. 241 | 242 | ![screenshot of the Ingest Dashboard showing the Destination/Logs tab for a Source named "demo"](../../static/img/ingest/destination-logs.png) 243 | 244 | Logs and statistics are available to help monitor for problems, replay or 245 | recover messages. 246 | 247 | 248 | ## Managing Source Tokens 249 | 250 | The last portion of the Ingest URL is a `Token` which can be 251 | invalidated and rotated: 252 | 253 | ![screenshot of the Ingest Dashboard showing the overview tab for a Source named "demo"](../../static/img/ingest/url-token-rotate.png) 254 | 255 | `Token`s that are rotated stay usable for 24 hours. During this time both the 256 | old and new `Token`s are honored by Ingest. During this period it's important 257 | that you reconfigure your provider with the new Ingest URL in order to have a 258 | seamless transition. 259 | 260 | [Svix Ingest]: https://svix.com/ingest 261 | [Ingest Dashboard]: https://dashboard.svix.com/ingest 262 | -------------------------------------------------------------------------------- /docs/receiving/source-ips.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Static Source IP Addresses 3 | --- 4 | 5 | import sourceIps from "/static/webhook-ips.json"; 6 | 7 | In case your webhook receiving endpoint is behind a firewall or NAT, you may need to allow traffic from Svix's static IP addresses. 8 | 9 | This is the full list of IP addresses that webhooks may originate from grouped by the region of the sender. The list is also available as a JSON file: [webhooks-ips.json](pathname:///webhook-ips.json). 10 | 11 | ## Regions 12 | 13 | The list of IP addresses below is grouped by the respective Svix region. The region where webhooks are sent from is the same as the region of respective environment. This means that all webhooks in the EU region will use the EU IP addresses below, all webhooks in the US region will use the US IP addressed below, and the same for every other region. 14 | 15 | ### US 16 | 17 |
18 | 
19 | {sourceIps["us"].join("\n")}
20 | 
21 | 
22 | 23 | ### EU 24 | 25 |
26 | 
27 | {sourceIps["eu"].join("\n")}
28 | 
29 | 
30 | 31 | ### US (Private region) 32 | 33 |
34 | 
35 | {sourceIps["us-east"].join("\n")}
36 | 
37 | 
38 | 39 | ### Australia (Private region) 40 | 41 |
42 | 
43 | {sourceIps["au"].join("\n")}
44 | 
45 | 
46 | 47 | ### Canada (Private region) 48 | 49 |
50 | 
51 | {sourceIps["ca"].join("\n")}
52 | 
53 | 
54 | 55 | ### India (Private region) 56 | 57 |
58 | 
59 | {sourceIps["in"].join("\n")}
60 | 
61 | 
62 | 63 | :::info[Important] 64 | Static source IPs are not guaranteed for all pricing tiers, please refer to [the pricing page](https://www.svix.com/pricing/) for more information. 65 | ::: 66 | 67 | ## Webhook User Agent 68 | 69 | Some systems may require to identify the User-Agent used by Svix in addition to the IP addresses. Requests from the Svix service, as well as self-hosted instances, may use different user-agent strings depending on the exact plan and configuration (usually `Svix-Webhooks/{server-version}` or `Webhooks/{server-version}`). 70 | 71 | However, every webhook coming from the Svix hosted service (using the IP addresses above), will also include `sender-9YMgn` in order to enable easy identification of requests coming from the hosted service. 72 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/adding-endpoints.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding Endpoints 3 | --- 4 | 5 | In order to start listening to messages sent through Svix, you will need to configure your **endpoints**. 6 | 7 | Adding an endpoint is as simple as providing a URL that you control and a list of **event types** that you want to 8 | listen to. 9 | 10 | ![add endpoint](/img/app-portal/add-endpoint.png) 11 | 12 | :::tip[Svix Play] 13 | 14 | If you don't have a URL or your service isn't quite ready to start receiving events just yet, 15 | just press the **use Svix Play** button to have a unique URL generated for you. 16 | 17 | You'll be able to view and inspect all operational webhooks sent to your Svix Play URL, making it 18 | effortless to get started. 19 | ::: 20 | 21 | When configuring your endpoint, make sure to take a look at the [Event Catalog](./event-catalog) to see the 22 | full descriptions of each endpoint as well as all the fields they send. 23 | 24 | If you don't specify any event types, by default, your endpoint will receive all events, regardless of type. 25 | This can be helpful for getting started and for testing, but we recommend changing this to a subset later on 26 | to avoid receiving unexpected messages. 27 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/event-catalog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Event Catalog 3 | --- 4 | 5 | The Event Catalog shows all the **event types** available to subscribe to. 6 | 7 | ![event catalog](/img/app-portal/event-catalog.png) 8 | 9 | From here, you can view the **event definition**–the shape of the event payload 10 | as well as what values you can expect to receive. 11 | 12 | In addition, you can see an example payload for that event type. 13 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/filtering-logs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Filtering Logs 3 | --- 4 | 5 | ![unfiltered message logs](/img/app-portal/log-list.png) 6 | 7 | If you need to track down a particular message that was sent to one of your 8 | endpoints, there are a few ways to help you find it. 9 | 10 | If you know what endpoint it went to, the **endpoint page** has a filtered 11 | list of all messages sent to it. You can filter this list by 12 | **event type**. If you know roughly when the message was sent, you can further 13 | narrow down the list using the **date filter**. 14 | 15 | ![date filter](/img/app-portal/date-filter.png) 16 | 17 | You can choose to filter from one of the presets: 18 | 19 | - 5 hours ago 20 | - yesterday 21 | - last week 22 | - last month 23 | - custom 24 | 25 | If you choose "custom", you can specify an exact date and time to jump back to 26 | in the message list. 27 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/polling-endpoints.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Polling Endpoints 3 | --- 4 | 5 | Polling Endpoints are a way to get a stream of events by polling, without having to set up a webhook endpoint. 6 | 7 | Start by creating an endpoint and select *Polling Endpoint* as the type. 8 | 9 | ![Polling Endpoint Create](../../img/advanced-endpoints/polling-endpoint-create.png) 10 | 11 | As with [regular webhook endpoints](/receiving/using-app-portal/adding-endpoints), you can control which event types and channels you want to receive. 12 | 13 | 14 | # Usage 15 | 16 | Once you've created a Polling Endpoint, you'll get a unique URL like `https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q`. 17 | 18 | You can call this endpoint directly once you have an [API key](/receiving/using-app-portal/polling-endpoints#api-keys). 19 | 20 | ```bash 21 | curl \ 22 | "https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q" \ 23 | -H 'Accept: application/json' \ 24 | -H 'Authorization: Bearer sk_poll_*****.eu' 25 | ``` 26 | 27 | When first calling the endpoint, it will return an empty array and an iterator. 28 | 29 | ```json 30 | { 31 | "data": [], 32 | "iterator": "eyJvZmZzZXQiOi05MjIzMzcyMDM2ODU0Nzc1ODA4LCJhZnRlciI6bnVsbH0", 33 | "done": false 34 | } 35 | ``` 36 | 37 | Then, using the iterator in the next call, you can iterate through the events and poll for new ones. 38 | 39 | ```bash 40 | curl \ 41 | -X GET "https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q?iterator=eyJv..." \ 42 | -H 'Accept: application/json' \ 43 | -H 'Authorization: Bearer sk_poll_*****.eu' 44 | ``` 45 | 46 | An example response might look like this: 47 | 48 | ```json 49 | { 50 | "data": [ 51 | { 52 | "id": "ev_1234567890", 53 | "type": "invoice.created", 54 | "created_at": 1715289600 55 | }, 56 | { 57 | "id": "ev_1234567891", 58 | "type": "invoice.updated", 59 | "created_at": 1715289601 60 | } 61 | ], 62 | "iterator": "aHR0cHM6Ly9hcGkuc3ZpeC5jb20vYXBpL3YxL2FwcC9hcHBfMm1HNkRnVWE=", 63 | "done": false 64 | } 65 | ``` 66 | 67 | ### API Keys 68 | 69 | To call the Polling Endpoint, you'll need to create an endpoint-specific API key. 70 | 71 | 72 |
73 |
74 | Polling Endpoint Create API Key 79 |
80 |
81 | Polling Endpoint Create API Key 86 |
87 |
88 | 89 | 90 | API keys are scoped exclusively to the endpoint they were created for, and they can be expired or rotated at any time. 91 | 92 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/replaying-messages.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Replaying Messages 3 | --- 4 | 5 | #### Why Replay 6 | 7 | - If your service has downtime 8 | - If your endpoint was misconfigured 9 | 10 | If you want to replay a single event, you can find the message 11 | from the UI and click the options menu next to any of the attempts. 12 | 13 | ![resend message](/img/app-portal/resend-single.png) 14 | 15 | From there, click "resend" to have the same message send to your 16 | endpoint again. 17 | 18 | If you need to recover from a service outage and want to replay all 19 | the events since a given time, you can do so from the Endpoint page. 20 | On an endpoint's details page, click `Options > Recover Failed Messages`. 21 | 22 | ![recover modal](/img/app-portal/replay-modal.png) 23 | 24 | From there, you can choose a time window to recover from. 25 | 26 | For a more granular recovery – for example, if you know the exact timestamp 27 | that you want to recover from – you can click the options menu on any message 28 | from the endpoint page. From there, you can click "Replay..." and choose to 29 | "Replay all failed messages since this time." 30 | 31 | --- 32 | 33 | With all the ways to resend and recover failed messages, you can be more 34 | confident in your endpoints and free to experiment with new event types. 35 | -------------------------------------------------------------------------------- /docs/receiving/using-app-portal/testing-events.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing Events 3 | --- 4 | 5 | The easiest way to be more confident in your endpoint configuration 6 | is to start receiving events as quickly as possible. 7 | 8 | That's why we have a "Testing" tab for you to send example events 9 | to your endpoint. 10 | 11 | ![testing endpoint](/img/app-portal/testing-endpoint.png) 12 | 13 | After sending an example event, you can click into the message to view 14 | the message payload, all of the message attempts, and whether it 15 | succeeded or failed. 16 | -------------------------------------------------------------------------------- /docs/receiving/verifying-payloads/how-manual.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Verifying Webhooks Manually 3 | --- 4 | 5 | import CodeTabs from '@theme/CodeTabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | :::info 9 | The recommended way to verify webhooks is using our official libraries as outlined in the [How to Verify](./how.mdx) section. 10 | ::: 11 | 12 | However, here are instructions for verifying manually for those who need it. 13 | 14 | ## Verifying signatures manually 15 | 16 | Each webhook call includes three headers with additional information that are used for verification: 17 | 18 | * `svix-id`: the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure). 19 | * `svix-timestamp`: timestamp in [seconds since epoch](https://en.wikipedia.org/wiki/Unix_time). 20 | * `svix-signature`: the [Base64](https://en.wikipedia.org/wiki/Base64) encoded list of signatures (space delimited). 21 | 22 | :::info 23 | Professional tier customers can have the headers white-labeled to use the `webhook-` prefix instead of the `svix-` prefix used above. The Svix libraries support both. 24 | ::: 25 | 26 | ### Constructing the signed content 27 | 28 | The content to sign is composed by concatenating the id, timestamp and payload, separated by the full-stop character (`.`). In code, it will look something like: 29 | 30 | ```javascript 31 | const signedContent = `${svix_id}.${svix_timestamp}.${body}`; 32 | ``` 33 | 34 | Where `body` is the raw body of the request. The signature is sensitive to any changes, so even a small change in the body will cause the signature to be completely different. This means that you should *not* change the body in any way before verifying. 35 | 36 | ### Determining the expected signature 37 | 38 | Svix uses an [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) with [SHA-256](https://en.wikipedia.org/wiki/SHA-2) to sign its webhooks. 39 | 40 | So to calculate the expected signature, you should HMAC the `signed_content` from above using the base64 portion of your signing secret (this is the part after the `whsec_` prefix) as the key. 41 | For example, given the secret `whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw` you will want to use `MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw`. 42 | 43 | For example, this is how you can calculate the signature in Node.js: 44 | 45 | ```javascript 46 | const crypto = require('crypto'); 47 | 48 | const signedContent = `${svix_id}.${svix_timestamp}.${body}`; 49 | const secret = "whsec_5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH"; 50 | 51 | // Need to base64 decode the secret 52 | const secretBytes = Buffer.from(secret.split('_')[1], "base64"); 53 | const signature = crypto 54 | .createHmac('sha256', secretBytes) 55 | .update(signedContent) 56 | .digest('base64'); 57 | 58 | console.log(signature); 59 | ``` 60 | 61 | This generated signature should match one of the ones sent in the `svix-signature` header. 62 | 63 | The `svix-signature` header is composed of a list of space delimited signatures and their corresponding version identifiers. The signature list is most commonly of length one. Though there could be any number of signatures. For example: 64 | 65 | ``` 66 | v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo= 67 | ``` 68 | 69 | Make sure to remove the version prefix and delimiter (e.g. `v1,`) before verifying the signature. 70 | 71 | Please note that to compare the signatures it's recommended to use a constant-time string comparison method in order to prevent timing attacks. 72 | 73 | ### Verify timestamp 74 | 75 | As mentioned above, Svix also sends the timestamp of the attempt in the `svix-timestamp` header. You should compare this timestamp against your system timestamp and make sure it's within your tolerance in order to prevent timestamp attacks. 76 | 77 | 78 | ### Example signatures 79 | 80 | Here is an example you can use to verify you implemented everything correctly. Please note that this may fail verification due to the timestamp being old. 81 | 82 | ```javascript 83 | secret = 'whsec_plJ3nmyCDGBKInavdOK15jsl'; 84 | payload = '{"event_type":"ping","data":{"success":true}}'; 85 | msg_id = 'msg_loFOjxBNrRLzqYUf'; 86 | timestamp = '1731705121'; 87 | 88 | // Would generate the following signature: 89 | signature = 'v1,rAvfW3dJ/X/qxhsaXPOyyCGmRKsaKWcsNccKXlIktD0='; 90 | ``` 91 | 92 | Additionally, you can use the [webhook simulation tool](https://www.standardwebhooks.com/simulate) to generate as many examples as you need. 93 | -------------------------------------------------------------------------------- /docs/receiving/verifying-payloads/receiving-with-bridge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Receiving Webhooks with Bridge 3 | --- 4 | 5 | import CodeTabs from '@theme/CodeTabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | 9 | [Svix Bridge] is useful in cases where you want to consume webhooks and write the payloads to a message queue. 10 | 11 | 12 | ## Using the Built-in HTTP Server 13 | 14 | Since Bridge can act as an HTTP server, you can configure it as an Endpoint in Svix. 15 | Bridge can even verify webhooks when configured with an `endpoint_secret`. 16 | 17 | For example, receiving webhooks from Svix, lightly reshaping the payload, then publishing to RabbitMQ might be 18 | configured like this: 19 | 20 | ```yaml 21 | receivers: 22 | - name: 'events-from-acme' 23 | input: 24 | type: 'svix-webhook' 25 | path_id: 'acme' 26 | endpoint_secret: '${ENDPOINT_SECRET}' 27 | 28 | transformation: | 29 | function handler(input) { 30 | let event_type = input.eventType; 31 | delete input.eventType; 32 | // The `payload` field is what will be published to the queue. 33 | return { payload: { event_type, ...input } }; 34 | } 35 | 36 | output: 37 | type: 'rabbitmq' 38 | uri: '${RABBITMQ_URI}' 39 | exchange: '' 40 | routing_key: 'acme' 41 | ``` 42 | 43 | The `path_id` defined here represents the trailing path segment for the route this receiver will match. 44 | The route for this example would be `/webhook/acme` and sending a `POST` request here with a valid JSON body will result 45 | in a new message published to the `acme` queue. 46 | 47 | This is to say, if you're running Bridge at `https://my-bridge.example.com`, the full URL you'd register as a Svix 48 | Endpoint would be `https://my-bridge.example.com/webhook/acme`. 49 | See [Adding Endpoints](../using-app-portal/adding-endpoints.mdx) for more on how to do this in the App Portal. 50 | 51 | 52 | ## Using A Polling Endpoint 53 | 54 | Bridge can consume messages sent to [Ingest](/receiving/receiving-with-ingest) or any Svix Application with a 55 | [Polling Endpoint](/advanced-endpoints/polling-endpoints) configured as a destination. 56 | 57 | This is a great option if you don't want to run a public-internet-facing HTTP server to receive webhooks. 58 | Since Bridge initiates requests from within your environment, you don't need to open any ports in your firewall! 59 | 60 | ```yaml 61 | receivers: 62 | - name: "msg-poller-to-rabbitmq-example" 63 | input: 64 | type: "svix-message-poller" 65 | # The consumer id should be unique per the app & sink. 66 | # This is used to track Bridge's position in the stream so it can resume where it left off 67 | # after restarting, etc. 68 | consumer_id: "my-consumer" 69 | # The app id, and sink id can be found in the URL for the corresponding Polling Endpoint. 70 | # Eg: 71 | # https://api.us.svix.com/api/v1/app/app_2mG6DgUaGlwCNdM5oRCUJec2kQC/poller/poll_59q 72 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ 73 | app_id: "app_XXXX" 74 | sink_id: "poll_XXXX" 75 | # The token can be seen in the UI for the corresponding Polling Endpoint. 76 | token: "${POLLING_ENDPOINT_TOKEN}" 77 | 78 | transformation: | 79 | function handler(input) { 80 | let event_type = input.eventType; 81 | delete input.eventType; 82 | // The `payload` field is what will be published to the queue. 83 | return { payload: { event_type, ...input } }; 84 | } 85 | 86 | output: 87 | type: "rabbitmq" 88 | uri: "${QUEUE_URI}" 89 | exchange: "" 90 | routing_key: "my_queue" 91 | ``` 92 | 93 | --- 94 | 95 | Check out the the project on [GitHub][Svix Bridge] for more on how to get started with Bridge. 96 | 97 | [Svix Bridge]: https://github.com/svix/svix-webhooks/tree/main/bridge 98 | -------------------------------------------------------------------------------- /docs/receiving/verifying-payloads/why.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Why Verify Webhooks 3 | --- 4 | 5 | import CodeTabs from '@theme/CodeTabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | 9 | Because of the way webhooks work, attackers can impersonate services by simply sending a fake webhook to an endpoint. Think about it: it's just an HTTP POST from an unknown source. This is a potential security hole for many applications, or at the very least, a source of problems. 10 | 11 | In order to prevent it, Svix signs every webhook and its metadata with a unique key for each endpoint. This signature can then be used to verify the webhook indeed comes from Svix, and only process it if it is. 12 | 13 | Another potential security hole is what's called replay attacks. A [replay attack](https://en.wikipedia.org/wiki/Replay_attack) is when an attacker intercepts a valid payload (including the signature), and re-transmits it to your endpoint. This payload will pass signature validation, and will therefore be acted upon. 14 | 15 | To mitigate this attack, Svix includes a timestamp for when the webhook attempt occurred. Our libraries automatically reject webhooks with a timestamp that are more than five minutes away (past or future) from the current time. This requires your server's clock to be synchronised and accurate, and it's recommended that you use [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) to achieve this. 16 | 17 | For additional information about webhook vulnerabilities, please refer to the [webhook security](/security) section of the docs. 18 | -------------------------------------------------------------------------------- /docs/retention.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Payload Retention 3 | --- 4 | 5 | import CodeTabs from "@theme/CodeTabs"; 6 | import TabItem from "@theme/TabItem"; 7 | 8 | Svix retains all message payloads (content) for a period of 90 days, after which they are deleted. This ensures that potentially sensitive and private information is not held indefinitely. 9 | 10 | ## Modifying the retention period 11 | 12 | While 90 days is a good default for most use-cases, in some cases, a shorter retention period is preferred. Since different messages may require different retention periods, Svix lets you control the wanted retention on a message by message basis. 13 | 14 | To change the retention period just pass the wanted retention period when creating the message by setting the `payload_retention_period` parameter to the wanted number of days. 15 | 16 | Here are a few examples for setting the retention to `14` days: 17 | 18 | 19 | 20 | 21 | ```js 22 | const svix = new Svix("AUTH_TOKEN"); 23 | await svix.message.create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", { 24 | eventType: "invoice.paid", 25 | eventId: "evt_Wqb1k73rXprtTm7Qdlr38G", 26 | payloadRetentionPeriod: 14, 27 | payload: { 28 | id: "invoice_WF7WtCLFFtd8ubcTgboSFNql", 29 | status: "paid", 30 | attempt: 2, 31 | }, 32 | }); 33 | ``` 34 | 35 | 36 | 37 | 38 | ```python 39 | svix = Svix("AUTH_TOKEN") 40 | svix.message.create( 41 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0", 42 | MessageIn( 43 | event_type="invoice.paid", 44 | event_id="evt_Wqb1k73rXprtTm7Qdlr38G", 45 | payload_retention_period=14, 46 | payload={ 47 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 48 | "status": "paid", 49 | "attempt": 2 50 | } 51 | ) 52 | ) 53 | ``` 54 | 55 | 56 | 57 | 58 | ```go 59 | svixClient := svix.New("AUTH_TOKEN", nil) 60 | svixClient.Message.Create(ctx, "app_Xzx8bQeOB1D1XEYmAJaRGoj0", &svix.MessageIn{ 61 | EventType: "invoice.paid", 62 | EventId: svix.NullableString("evt_Wqb1k73rXprtTm7Qdlr38G"), 63 | PayloadRetentionPeriod: 14, 64 | Payload: map[string]interface{}{ 65 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 66 | "status": "paid", 67 | "attempt": 2, 68 | }, 69 | }) 70 | ``` 71 | 72 | 73 | 74 | 75 | ```rust 76 | let svix = Svix::new("AUTH_TOKEN".to_string(), None); 77 | svix.message() 78 | .create( 79 | "app_Xzx8bQeOB1D1XEYmAJaRGoj0".to_string(), 80 | MessageIn { 81 | event_type: "invoice.paid".to_string(), 82 | event_id: Some("evt_Wqb1k73rXprtTm7Qdlr38G".to_string()), 83 | payload: json!({ 84 | "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", 85 | "status": "paid", 86 | "attempt": 2 87 | }), 88 | ..MessageIn::default() 89 | }, 90 | None, 91 | ) 92 | .await?; 93 | ``` 94 | 95 | 96 | 97 | 98 | ```java 99 | Svix svix = new Svix("AUTH_TOKEN"); 100 | svix.getMessage().create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", 101 | new MessageIn() 102 | .eventType("invoice.paid") 103 | .eventId("evt_Wqb1k73rXprtTm7Qdlr38G") 104 | .payloadRetentionPeriod(14) 105 | .payload("{" + 106 | "\"id\": \"invoice_WF7WtCLFFtd8ubcTgboSFNql\"," + 107 | "\"status\": \"paid\"," + 108 | "\"attempt\": 2" + 109 | "}")); 110 | ``` 111 | 112 | 113 | 114 | 115 | ```kotlin 116 | val svix = Svix("AUTH_TOKEN") 117 | svix.message.create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", 118 | MessageIn( 119 | eventType = "invoice.paid", 120 | eventId = "evt_Wqb1k73rXprtTm7Qdlr38G")), 121 | payloadRetentionPeriod = 14, 122 | payload = mapOf( 123 | "id" to "invoice_WF7WtCLFFtd8ubcTgboSFNql", 124 | "status" to "paid", 125 | "attempt" to 2 126 | ) 127 | ``` 128 | 129 | 130 | 131 | 132 | ```ruby 133 | svix = Svix::Client.new("AUTH_TOKEN") 134 | svix.message.create("app_Xzx8bQeOB1D1XEYmAJaRGoj0", 135 | Svix::MessageIn.new({ 136 | "event_type" => "invoice.paid", 137 | "event_id" => "evt_Wqb1k73rXprtTm7Qdlr38G"})), 138 | "payload" => { 139 | "id" => "invoice_WF7WtCLFFtd8ubcTgboSFNql", 140 | "status" => "paid", 141 | "attempt" => 2 142 | }, 143 | "payload_retention_period" => 14 144 | }) 145 | ) 146 | ``` 147 | 148 | 149 | 150 | 151 | ```csharp 152 | var svix = new SvixClient("AUTH_TOKEN", new SvixOptions("https://api.us.svix.com")); 153 | await svix.Message.CreateAsync("app_Xzx8bQeOB1D1XEYmAJaRGoj0", new MessageIn( 154 | eventType: "invoice.paid", 155 | eventId: "evt_Wqb1k73rXprtTm7Qdlr38G", 156 | payloadRetentionPeriod: 14, 157 | payload: new { 158 | id: "invoice_WF7WtCLFFtd8ubcTgboSFNql", 159 | status: "paid", 160 | attempt: 2, 161 | }, 162 | )); 163 | ``` 164 | 165 | 166 | 167 | 168 | ```shell 169 | export SVIX_AUTH_TOKEN="AUTH_TOKEN" 170 | svix message create app_Xzx8bQeOB1D1XEYmAJaRGoj0 '{ "eventType": "invoice.paid", "eventId": "evt_Wqb1k73rXprtTm7Qdlr38G", "payloadRetentionPeriod": 14, "payload": { "id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", "status": "paid", "attempt": 2 } }' 171 | ``` 172 | 173 | 174 | 175 | 176 | ```shell 177 | curl -X POST "https://api.us.svix.com/api/v1/app/app_Xzx8bQeOB1D1XEYmAJaRGoj0/msg/" \ 178 | -H "Accept: application/json" \ 179 | -H "Content-Type: application/json" \ 180 | -H "Authorization: Bearer AUTH_TOKEN" \ 181 | -d '{"eventType": "invoice.paid", "eventId": "evt_Wqb1k73rXprtTm7Qdlr38G", "payloadRetentionPeriod": 14, "payload": {"id": "invoice_WF7WtCLFFtd8ubcTgboSFNql", "status": "paid", "attempt": 2}}' 182 | ``` 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /docs/retries.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Retry Schedule 3 | --- 4 | 5 | Svix attempts to deliver each webhook message based on a retry schedule with exponential backoff. 6 | 7 | 8 | ## The schedule 9 | 10 | Each message is attempted based on the following schedule, where each period is started following the failure of the preceding attempt: 11 | 12 | - Immediately 13 | - 5 seconds 14 | - 5 minutes 15 | - 30 minutes 16 | - 2 hours 17 | - 5 hours 18 | - 10 hours 19 | - 10 hours (in addition to the previous) 20 | 21 | If an endpoint is removed or disabled delivery attempts to the endpoint will be disabled as well. 22 | 23 | For example, an attempt that fails three times before eventually succeeding will be delivered roughly 35 minutes and 5 seconds following the first attempt. 24 | 25 | 26 | ## Indicating successful delivery 27 | 28 | The way to indicate that a webhook has been processed is by returning a `2xx` (status code `200-299`) response to the webhook message within a reasonable time-frame (15s with Svix). Any other status code, including `3xx` redirects are treated as failures. 29 | 30 | 31 | ## Failed delivery handling 32 | 33 | After the conclusion of the above attempts the message will be marked as `Failed` for this endpoint, and the webhook sender's account will get [an operational webhook](./incoming-webhooks.mdx) of type `message.attempt.exhausted` notifying them of this error. 34 | 35 | 36 | ## Disabling failing endpoints 37 | 38 | If all attempts to a specific endpoint fail for a period of 5 days, the endpoint will be disabled and an operational webhook (`EndpointDisabledEvent`) will be sent to your account. 39 | The clock only starts after multiple deliveries failed within a 24 hour span, with at least 12 hours difference between the first and the last failure. 40 | 41 | You can disable this behavior from the [Environment settings page](https://dashboard.svix.com/settings/organization/general-settings) on the dashboard. 42 | 43 | 44 | ## Manual retries 45 | 46 | Your customers can also use the application portal to manually retry each message at any time, or automatically retry ("Recover") all failed messages starting from a given date. 47 | 48 | Alternatively, you can use the respective APIs: 49 | - [Resend webhook](https://api.svix.com/docs#operation/resend_webhook_api_v1_app__app_id__msg__msg_id__endpoint__endpoint_id__resend__post) for retrying a single message. 50 | - [Resend failed webhooks](https://api.svix.com/docs#operation/recover_failed_webhooks_api_v1_app__app_id__endpoint__endpoint_id__recover__post) for the failed messages recovery. 51 | -------------------------------------------------------------------------------- /docs/security.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Webhook Security 3 | --- 4 | 5 | Webhooks are a very powerful tool, and when used correctly are also very secure. Svix automatically takes care of these security aspects for you, and offers easy to use library for verifying webhook integrity. For more information, please refer to the [webhook verification section](/receiving/verifying-payloads/why) of the webhook consumption docs. 6 | 7 | For more information about the security measures taken by the Svix service, please refer to the [Svix security page](https://www.svix.com/security/). 8 | 9 | :::info[Optional reading ahead] 10 | The section below is not required for the normal operation of the Svix service. It's only included here for educational purposes. 11 | ::: 12 | 13 | ## Server-side request forgery (SSRF) 14 | 15 | A server-side request forgery (SSRF) attack, is when an attacker abuses functionality on the server to read or update internal resources. In the attack, the attacker supplies or modifies a URL which the server will then make a call to. By carefully selecting the URLs, the attacker may be able to read server configuration such as AWS metadata, connect to internal services like http enabled databases or perform post requests towards internal services which are not intended to be exposed. 16 | 17 | Webhooks implementations are especially vulnerable to SSRF as they let their consumers (customers) add any URLs they want, which will be called from the internal webhook system. 18 | 19 | For more information about SSRF please refer to the [OWASP entry](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery). 20 | 21 | ### Preventing SSRF 22 | 23 | The main way to protect against SSRF is to prevent the webhooks from calling into internal networks and services. To achieve this you'd want to do two things: the first would be to proxy all webhook requests through a special proxy (like [smokescreen](https://github.com/stripe/smokescreen)) that filters internal IP addresses, and the second would be to put the webhook workers (or proxy) in their own private subnet that can't access internal services. 24 | 25 | ## Spoofing attacks 26 | 27 | Because of the way webhooks work, attackers can impersonate services by simply sending a fake webhook to an endpoint. Think about it: it's just an HTTP POST from an unknown source. 28 | 29 | 30 | ### Preventing spoofing attacks 31 | 32 | There are a few ways to prevent or reduce the likelihood of spoofing attacks, though the recommended (and wholly sufficient) way is by signing webhooks. 33 | 34 | #### Signature verification 35 | 36 | When signing the webhooks it's important to sign not only the payload, but also additional metadata such as idempotency ID and timestamp (more on those later). The signature scheme needs to use strong cryptographic primitives and used correctly. Some implementations use asymmetric signing using Ed25519 or RSA, but it's much more common (and recommended) to use `HMAC-SHA256` which is what Svix uses by default. 37 | 38 | For more information, please refer to the [webhook verification](/receiving/verifying-payloads/how-manual) docs. 39 | 40 | #### Authentication tokens 41 | 42 | Like normal HTTP requests, webhooks can authenticate using a pre-shared authentication token, HTTP Basic Auth, or any other common HTTP authentication method. This is not recommended for a variety of reasons, the first one is the danger of giving the webhook provider a token with more access than needed (as in, a token that can also make API calls) which is a common pitfall, and the other is that because of how webhooks work, the server can potentially be tricked to send the request to the wrong destination, exposing the token. 43 | 44 | 45 | #### IP allow list 46 | 47 | An IP allow list is an insecure mechanism when used on its own, as it's not meant for authentication. Svix supports sending from a specific set of source IPs. The reason why Svix supports it, is so that webhook consumers that have firewalls can add Svix to the allow list to ensure delivery. 48 | 49 | Relying on it however is insufficient, because depending on the cloud provider you use, you may be sharing IPs with other machines. Even if you have your own private IPs, you may accidentally release an IP address back to the cloud provider which can then be used to send malicious requests, and last but not least, this means that one customer of the service can trigger webhooks affecting another customer (because the sender will be from the same set of IP addresses) making the security insufficient. 50 | 51 | ## Replay attacks 52 | 53 | A replay attack is an attack when a valid data transmission is duplicated maliciously or fraudulently. 54 | 55 | A bad actor can intercept (and potentially modify) a request before it reaches its destination, then replay it. Even if the payload is encrypted or signed and can't be edited, simply repeating a request can cause a lot of problems (e.g. duplicating a purchase event). 56 | 57 | ### Preventing replay attacks 58 | 59 | #### Timestamps in the signature 60 | 61 | One way to limit the risks of replay attacks is by adding a timestamp to each webhook attempt and having the consumers verify it and ensuring it falls within a specific allowed tolerance. This method requires that the consumer has the correct time set on their machine (usually using NTP), and that the timestamp is also signed by the signature mentioned above (otherwise it can be spoofed). 62 | 63 | #### Idempotency 64 | 65 | With respect to APIs, idempotence means that you would get the same result whether you make an identical API call once or many times. If your endpoints are idempotent, any webhook requests will only be processed once, even if they are received multiple times. This can be done either implicitly (e.g. the content of the webhook is such that processing it multiple times doesn't make sense) or explicitly, which means adding a special ID for each message which the consumer can validate against. 66 | 67 | 68 | ## Man-in-the-middle attack 69 | 70 | Man-in-the-middle (MITM) attack is a form of eavesdropping in which the attacker secretly relays and possibly alters the communications between two parties who believe they are directly communicating with each other. 71 | 72 | ### Preventing man-in-the-middle attacks 73 | 74 | The most common way to avoid a MITM attack is to always use HTTPS URLs to ensure that the request is encrypted and the connection is verified. 75 | 76 | 77 | ## What about mTLS? 78 | 79 | By default the TLS protocol only proves the identity of the server to the client, and the authentication of the client to the server is left to the application layer. TLS also offers client-to-server authentication using client-side certificates. 80 | 81 | While it solves most of the above issues, it's rarely used as it puts a massive burden on webhook consumers. The problem is that it requires provisioning of client certifications for usage with the webhook receiver, and requires the consumer to know how to handle mTLS which is not possible in most managed cloud environments. 82 | 83 | In addition, unless a specific certificate is created for each endpoint, it suffers from the same issue that the "IP allow list" solution suffers from above: different customers of the same webhook service can maliciously trigger authenticated webhooks to one another. 84 | -------------------------------------------------------------------------------- /docs/sending-messages-with-bridge.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sending Messages with Bridge 3 | --- 4 | 5 | import EventTypeTabs from '@theme/EventTypeTags'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | 9 | If you have messages moving through queues that you'd like to send as webhooks, [Svix Bridge] can help. 10 | 11 | Bridge is an agent that can read from a queue, transform the message with a JavaScript function, then forward to Svix for delivery. 12 | With a small amount of configuration, you can start consuming events, reshaping, and sending webhooks in minutes. 13 | 14 | For example, reading from RabbitMQ, modifying the payload in JS, then sending to Svix might look like this: 15 | 16 | ```yaml 17 | senders: 18 | - name: 'send-from-rabbitmq-example' 19 | input: 20 | type: 'rabbitmq' 21 | uri: '${RABBITMQ_URI}' 22 | queue_name: '${QUEUE_NAME}' 23 | 24 | transformation: | 25 | function handler(input) { 26 | return { 27 | appId: input.key, 28 | message: { 29 | eventType: input.event_type, 30 | payload: input.data 31 | } 32 | }; 33 | } 34 | 35 | output: 36 | type: 'svix' 37 | token: '${SVIX_TOKEN}' 38 | ``` 39 | 40 | In this example, we're subscribing to messages from a queue, making the payload conform to the expected shape, 41 | including the required fields: 42 | 43 | - `appId` 44 | - `message.eventType` 45 | - `message.payload` 46 | 47 | 48 | Check out the the project on [GitHub][Svix Bridge] for more on how to get started with Bridge. 49 | 50 | [Svix Bridge]: https://github.com/svix/svix-webhooks/tree/main/bridge 51 | -------------------------------------------------------------------------------- /docs/transformations.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Transformations 3 | --- 4 | 5 | Transformations are a powerful Svix feature that allows the modification of certain webhook properties in-flight. When you enable Transformations, your customers can write JavaScript code on their endpoints that can change a webhook's HTTP method, target URL, and body payload. 6 | 7 | ## Enabling Transformations 8 | 9 | Transformations can be enabled at the environment level. When you enable Transformations for an environment, your customers will be able to use Transformations on their endpoints. 10 | 11 | To enable Transformations, navigate to the Settings page in the dashboard. Under the Environment Settings section, click General Settings, and then toggle the Enable Transformations switch. Your customers will need to reenter the Consumer App Portal for the change to take effect. 12 | 13 | ![Environment Settings page with the Enable Transformation toggle](./img/enable-transformations.png) 14 | 15 | ## Using Transformations 16 | 17 | Once enabled for an environment, you customers can begin using Transformations by logging into the Consumer Portal, clicking on an endpoint, clicking into the Advanced tab, and scrolling down to the Transformations card: 18 | 19 | ![The Transformations card on an endpoint](./img/transformations-card.png) 20 | 21 | An endpoint's Transformation can be enabled or disabled at any time by toggling the switch on this card. 22 | 23 | Your customers can write Javascript code to edit an endpoint's Transformation, and test their code against an event type's payload or a custom payload to see the resulting webhook. 24 | 25 | ![Editing an endpoint's Transformation](./img/edit-transformation.png) 26 | 27 | ### How to write a Transformation 28 | 29 | Svix expects a Transformation to declare a function named `handler`. Svix will pass a `WebhookObject` to this function as its only argument, and expects the function to always return a `WebhookObject`. 30 | 31 | `WebhookObject` is a JSON object containing 4 properties: 32 | 33 | - `method`, a string representing the HTTP method the webhook will be sent with. It is always `"POST"` by default, and its only valid values are `"POST"` or `"PUT"` 34 | - `url`, a string representing the endpoint's URL. It can be changed to any valid URL. 35 | - `payload`, which contains the webhook's payload as a JSON object. It can be changed as needed. 36 | - `cancel`, a Boolean which controls whether or not to cancel the dispatch of a webhook. This value defaults to `false`. Note that canceled messages appear as successful dispatches. 37 | 38 | The Transformation will only work if the `handler` function returns the modified `WebhookObject`. 39 | 40 | ### An example Transformation 41 | 42 | Suppose that sometimes, your customer wants to redirect webhooks to a custom URL instead of the endpoint's defined URL. They only want to do this redirect if a custom URL is present in the webhook payload. They can write a transformation like this: 43 | 44 | ```js 45 | function handler(webhook) { 46 | if (webhook.payload.customUrl) { 47 | webhook.url = webhook.payload.customUrl; 48 | } 49 | return webhook; 50 | } 51 | ``` 52 | 53 | Great, the webhook is redirected to the custom URL if the `customUrl` property exists on the payload. Otherwise, it is sent to the endpoint's defined URL. 54 | -------------------------------------------------------------------------------- /docs/tutorials/api-webhook-management.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Webhooks API for your customers 3 | --- 4 | 5 | import CLITabs from '@theme/CLITabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | While most Svix customers use the [pre-built application portal](../app-portal.mdx) to let their webhook consumers manage their webhook subscriptions. In some scenarios it may be desirable to give webhook consumers the ability to manage their subscriptions using an API. 9 | 10 | The most effective way to support this use case is by incorporating webhook management endpoints into your existing API. This allows your webhook consumers to use the same API they're already familiar with and continue using their existing authentication tokens. 11 | 12 | You can achieve this by implementing thin wrappers around the Svix API that verify the authentication tokens and then make calls to the respective Svix APIs. Here is a small Python example showing how to wrap the [create endpoint API](https://api.svix.com/docs#tag/Endpoint/operation/v1.endpoint.create): 13 | 14 | ```python 15 | from fastapi import FastAPI, HTTPException, Depends 16 | from pydantic import BaseModel 17 | from svix import Svix, EndpointIn 18 | from .auth import get_authenticated_user, User 19 | 20 | app = FastAPI() 21 | svix = Svix("AUTH_TOKEN") 22 | 23 | class EndpointRequest(BaseModel): 24 | url: str 25 | description: str 26 | # Add as many fields as you want here 27 | 28 | @app.post("/endpoint") 29 | def create_endpoint(data: EndpointRequest, user: User = Depends(get_authenticated_user)): 30 | try: 31 | return svix.endpoint.create( 32 | user.id, # Using the user id as the app UID 33 | EndpointIn(url=data.url, description=data.description) 34 | ) 35 | except Exception as e: 36 | raise HTTPException(status_code=500, detail=str(e)) 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/tutorials/cli.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with the CLI 3 | --- 4 | 5 | import CLITabs from '@theme/CLITabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | The Svix CLI is one of the fastest ways to interact with our API, which is especially useful during development. 9 | 10 | With the Svix CLI you can interact with our webhook sending API, validate webhook payloads, quickly open our docs, and much more. 11 | 12 | In this tutorial, we will learn how to install the Svix CLI and use it to send your first webhook message! 13 | 14 | :::info 15 | This tutorial assumes you have already familiar with the [Svix webhooks service](https://www.svix.com). If this is your first time using Svix, 16 | we recommend you first check out our [quickstart documentation](https://docs.svix.com/overview). 17 | ::: 18 | 19 | ## Install the Svix CLI 20 | 21 | Before you go any further, you need to [install the Svix CLI](https://github.com/svix/svix-cli). 22 | 23 | 24 | 25 | 26 | To install the Svix CLI with [Homebrew](https://brew.sh), run: 27 | 28 | 29 | 30 | ```sh 31 | brew install svix/svix/svix-cli 32 | ``` 33 | 34 | 35 | 36 | To install the Svix CLI with [Scoop](https://scoop.sh/), run: 37 | 38 | 39 | ```sh 40 | scoop bucket add svix https://github.com/svix/scoop-svix.git 41 | ``` 42 | 43 | ```sh 44 | scoop install svix 45 | ``` 46 | 47 | 48 | 49 | 50 | To install the Svix CLI on macOS without Homebrew: 51 | 52 | 1. Download the latest Darwin tar.gz file from https://github.com/svix/svix-cli/releases/latest 53 | 54 | 2. Extract the file: `tar -xvf svix_X.X.X_Darwin_x86_64.tar.gz` 55 | 56 | 2. Optionally, install the binary in a location where you can execute it globally (e.g., /usr/local/bin). 57 | 58 | 59 | 60 | 61 | To install the Svix CLI on Linux without a package manager: 62 | 63 | 1. Download the latest Linux tar.gz file from https://github.com/svix/svix-cli/releases/latest 64 | 65 | 2. Extract the file: `tar -xvf svix_X.X.X_Linux_x86_64.tar.gz` 66 | 67 | 3. Run the executable: `./svix` 68 | 69 | 70 | 71 | 72 | To install the Svix CLI on Windows without Scoop: 73 | 74 | 1. Download the latest Windows tar.gz file from https://github.com/svix/svix-cli/releases/latest 75 | 76 | 2. Extract the `svix_X.X.X_Windows_x86_64.tar.gz` file. 77 | 78 | 3. Run the `.exe` file 79 | 80 | 81 | 82 | 83 | ## Authenticate with your Svix account 84 | 85 | After installing the Svix CLI, you must authenticate with your Svix account before you can start running commands. 86 | 87 | To do so, simply set your Svix Auth Token to the `SVIX_AUTH_TOKEN` env variable: 88 | 89 | - On macOS and Linux: `export SVIX_AUTH_TOKEN=` 90 | - On Windows via command prompt: `set SVIX_AUTH_TOKEN=` 91 | - On Windows via PowerShell: `$env:SVIX_AUTH_TOKEN = ''` 92 | 93 | ## Create an application 94 | 95 | You’re now ready to use the Svix CLI. To get started let's create a new application. 96 | 97 | Each Svix CLI command accepts raw JSON input as either the first positional argument or piped via `stdin`. 98 | 99 | We can create an application with the `application create` command: 100 | 101 | ```sh 102 | svix application create '{"name": "My Application"}' 103 | ``` 104 | 105 | API commands also include convenience flags for common values to make it easier when handcrafting commands. 106 | For example, the same application could have been created using the `--data-name` flag: 107 | 108 | ```sh 109 | svix application create --data-name "My Application" 110 | ``` 111 | 112 | This command creates a new application on Svix and returns the raw application object as JSON. 113 | 114 | ## List your applications 115 | 116 | Now that we've created an app we can list our existing apps with the following command: 117 | 118 | ```sh 119 | svix application list 120 | ``` 121 | 122 | The list command by default returns the first 50 applications, you can change this limit by adding the `--limit` flag 123 | 124 | ```sh 125 | # Get the first 100 applications 126 | svix application list --limit 100 127 | ``` 128 | 129 | ## Add an endpoint 130 | 131 | Next let's add an endpoint to the application we created above. The create command should have returned an Application ID. 132 | Since the application name is not unique, we will need its ID to make changes. 133 | 134 | Let's add a webhook endpoint with the url `http://example.com/webhook`. Every endpoint must have a version associated with it 135 | to help you version your webhook API. For this example we will set the version to `1`. 136 | 137 | ```sh 138 | svix endpoint create \ 139 | --data-url http://example.com/webhook \ 140 | --data-version 1 141 | ``` 142 | 143 | ## Send a message 144 | 145 | Now that we have an application created and an endpoint configured to listen to messages from that app, we are ready to send our first 146 | webhook message! Let's send a `user.created` event with the user's username and email. 147 | 148 | ```sh 149 | svix message create \ 150 | --data-eventType "user.created" \ 151 | --data-payload '{ 152 | "username": "new_user", 153 | "email": "new_user@example.com" 154 | }' 155 | ``` 156 | 157 | This message will now be sent to Svix and dispatched to all webhook endpoints listening to your application. 158 | 159 | ## Get creative 160 | 161 | The Svix CLI has commands to interact with every part of the Svix API. 162 | 163 | To get a complete list of commands, run `svix --help`. 164 | 165 | Since each command accepts raw JSON input piped over `stdin`, you can chain Svix CLI commands together with other command line tools (like the popular JSON parser 166 | [jq](https://stedolan.github.io/jq/)) to create powerful snippets to improve your workflow. 167 | 168 | For example, to automatically open the dashboard for the app we created in step 3, on macOS you can run the following command: 169 | 170 | 171 | ```sh 172 | svix application get | jq '.id' | xargs svix auth dashboard | jq '.url' | xargs open 173 | ``` 174 | 175 | With a slight modification, we can convert this one-liner to a shell function: 176 | 177 | ```zsh 178 | function dashboard { 179 | svix application get "$1" | jq '.id' | xargs svix auth dashboard | jq '.url' | xargs open 180 | } 181 | ``` 182 | 183 | Which you can run like so: 184 | 185 | ```zsh 186 | dashboard 187 | ``` 188 | -------------------------------------------------------------------------------- /docs/tutorials/connectors.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuring the Slack Connector 3 | --- 4 | 5 | :::info[Prerequisites] 6 | This guide assumes your familiar with the basics of [event types](../event-types), the [App Portal](../app-portal) and [Connectors](../connectors). 7 | ::: 8 | 9 | [Connectors](https://docs.svix.com/connectors) make it easy for your customers to link your webhooks to popular services like Slack, Hubspot, or Microsoft Teams in just a few clicks—no code or new backend infrastructure required. 10 | 11 | With the Slack connector, you can offer your users an easy-to-use way of integrating your webhooks with Slack, with almost no engineering effort on your part. 12 | 13 | ![Connectors diagram](../img/tutorials/slack-connectors/with-without-connectors.svg) 14 | 15 | ## Creating a Slack connector 16 | 17 | 1. Go to the [Connectors page](https://dashboard.svix.com/connectors) and click on "Configure Connector". 18 | 19 | 2. Add a description. This will be displayed to your users in the App Portal. 20 | 21 | 3. Select the event types this connector will support. You can choose specific event types that make sense for Slack, or select all of them. 22 | 23 | 4. Write the transformation code. This is the code that will be used to transform the webhook payload into a format that the Slack Incoming Webhooks API uses. 24 | 25 | ## Writing the transformation 26 | 27 | Transformations allow the modification of certain webhook properties in-flight using Javascript code. The transformation is the key component that makes your connector work. 28 | 29 | For the Slack connector, the transformation should make sure the webhook payload is formatted for the [Slack Incoming Webhooks API](https://api.slack.com/messaging/webhooks#posting_with_webhooks), for each of the event types you support (step 3). 30 | 31 | For example, let's say you have two event types, `invoice.created` and `invoice.paid`, with the following schemas: 32 | 33 | ```json 34 | { 35 | "invoice.created": { 36 | "type": "object", 37 | "properties": { 38 | "amount": { 39 | "type": "number" 40 | }, 41 | "name": { 42 | "type": "string" 43 | } 44 | } 45 | }, 46 | "invoice.paid": { 47 | "type": "object", 48 | "properties": { 49 | "invoice_id": { 50 | "type": "string" 51 | }, 52 | "amount": { 53 | "type": "number" 54 | } 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | Your transformation should `switch` on the `webhook.eventType` to determine how to use the payload and transform it for the Slack API. 61 | 62 | ```js 63 | /** 64 | * @param webhook the webhook object 65 | * @param webhook.method destination method. Allowed values: "POST", "PUT" 66 | * @param webhook.url current destination address 67 | * @param webhook.eventType current webhook Event Type 68 | * @param webhook.payload JSON payload 69 | * @param webhook.cancel whether to cancel dispatch of the given webhook 70 | */ 71 | function handler(webhook) { 72 | switch (webhook.eventType) { 73 | case "invoice.created": 74 | webhook.payload = { 75 | text: `New invoice created: ${webhook.payload.amount}` 76 | }; 77 | break; 78 | case "invoice.paid": 79 | // You can also use advanced formatting options (see https://api.slack.com/messaging/webhooks#advanced_message_formatting) 80 | webhook.payload = { 81 | text: `Invoice paid: ${webhook.payload.invoice_id}`, 82 | blocks: [ 83 | { 84 | type: "section", 85 | text: { 86 | type: "mrkdwn", 87 | text: `Invoice paid: ${webhook.payload.invoice_id}` 88 | } 89 | }, 90 | { 91 | type: "actions", 92 | elements: [ 93 | { 94 | type: "button", 95 | text: { 96 | type: "plain_text", 97 | text: "View in dashboard" 98 | }, 99 | url: `https://dashboard.invoices.com/invoices/${webhook.payload.invoice_id}` 100 | } 101 | ] 102 | } 103 | ] 104 | }; 105 | break; 106 | } 107 | return webhook; 108 | } 109 | ``` 110 | 111 | :::info 112 | Make sure your transformation works for the selected event types without any modifications, so your customers can use the connector in a few clicks. They will still be able to modify it if they want to. 113 | ::: 114 | 115 | ## Testing the connector 116 | 117 | Now that you've created your Slack connector, you can test it out in the App Portal. 118 | 119 | [Create a sample application](https://docs.svix.com/quickstart#creating-a-consumer-application) and use the 'Preview App Portal' button in the Svix Dashboard. 120 | 121 | In the App Portal, the new connector will be visible when creating a new endpoint. 122 | 123 | ![Slack connector in App Portal](../img/tutorials/slack-connectors/new-endpoint.png) 124 | 125 | When creating an endpoint, your connector should work without any further changes, so just click create and test it! 126 | 127 | ![Testing](../img/tutorials/slack-connectors/testing.png) 128 | 129 | After sending a test event, we should see the message directly in Slack: 130 | 131 | ![Slack message](../img/tutorials/slack-connectors/slack-notification.png) 132 | 133 | And the message using advanced formatting: 134 | 135 | ![Slack message advanced](../img/tutorials/slack-connectors/advanced-formatting.png) 136 | 137 | And that's it! In just a few clicks, your users will be able to connect their Slack account and start receiving notifications from your webhook events. 138 | 139 | ## Wrapping up 140 | 141 | All we needed to do to setup the connector was select the event types we wanted to support and write the transformation. Svix takes care of the rest, so you can provide a one-click connection to Slack for your users. 142 | 143 | Setting up connectors for the other supported services is just as easy. Check out the [Connectors docs](../connectors) to learn more. 144 | 145 | -------------------------------------------------------------------------------- /docs/tutorials/event-type-schema.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Your first event type schema 3 | --- 4 | 5 | :::note[Prerequisites] 6 | This guide assumes your familiar with the basics of [event types](../event-types) and the [App Portal](../app-portal). 7 | ::: 8 | 9 | The key to providing an easy webhook integration experience for your users lays in your event types. 10 | The first thing your users are interested in seeing when integrating with you is: what events do you send and what information do the events provide. 11 | 12 | It's very important that users can answer these two questions just by looking at your event types. 13 | 14 | Let's walk through creating a new event type with Svix. 15 | 16 | ## Choosing an event type name 17 | 18 | Let's imagine we are defining webhooks for when an organization within your service changes. Webhook consumers might be interested in when a user is added or removed from the organization. Or when the organization settings are changed. It's important to imagine what hypothetical endpoints a user might have listening to those events. 19 | 20 | Your consumers might have one endpoint for organization member changes and one for listening to settings changes. 21 | 22 | To make the event types discoverable, let's define them as follows: 23 | 24 | - `organization.member.added` 25 | - `organization.member.deleted` 26 | - `organization.member.modified` 27 | - `organization.settings.modified` 28 | 29 | We have defined logic groups of event types and dot-delimited them. Svix understands this syntax and will group them visually in the App Portal UI. 30 | 31 | ## Defining the schema 32 | 33 | Now we want our users to know just exactly what they can expect to see in the message payload for `organization.member.added`. 34 | 35 | So let's add a schema to the event type: 36 | 37 | ![add-schema](../img/schema-add-schema.png) 38 | 39 | ### Option A. Using the visual editor 40 | 41 | To get started with the visual editor, let's add some child fields. We need a field for `account_id`, `first_name`, `last_name` and `created_at`. 42 | 43 | To create a new child field, click "Add child" from under the parent field. Each field row has the following properties: 44 | 45 | - field name 46 | - type 47 | - title 48 | - is required? 49 | 50 | First, fill in the field names. You'll notice that the field titles will automatically update to match the field name. You can choose to override this with your own title if you'd like. Next, since all of these fields will always be present, make sure to check the "is required" checkboxes next to each field. 51 | 52 | Finally, we can make the field data types more specific. `created_at` isn't just a string, we also know the format of the string. To specify the format, click the gear icon to the right of the field. You should see a list of configuration options for the `created_at` field. Change the format to `date-time` and add a description here if you'd like. 53 | 54 | Once you've defined this basic schema, save your event type. The event type detail view will now show you a preview of the schema that matches the interface your users will see in the Event Catalog. 55 | 56 | #### Adding a custom example 57 | 58 | Next to the schema preview, you should see an example event defined. While this example is based on the schema parameters you defined, it might not be the best representation of reality. 59 | 60 | For this reason, it's best to provide your own custom example. 61 | 62 | To do so, go back into edit mode for the schema. 63 | 64 | From there, click "Configure example". In the example textarea, you can change the example to be something like the following: 65 | 66 | ```json 67 | { 68 | "account_id": "acct_0123456", 69 | "first_name": "Bruce", 70 | "last_name": "Wayne", 71 | "created_at": "2021-09-22T17:47:43.575Z" 72 | } 73 | ``` 74 | 75 | Then click "Save" to see your changes reflected in the schema preview and the Event Catalog. 76 | 77 | ### Option B. Providing a JSONSchema 78 | 79 | Instead of using the visual editor, you can also choose to provide your own JSONSchema. If you already have a JSONSchema (or JSONSchema-compatible definition) defined for your events, this is likely your fastest option. 80 | 81 | When you are configuring your schema, you can switch to the "Code" tab to fill out your own JSONSchema (Draft 7) spec. 82 | 83 | ![schema-add-code](../img/schema-add-code.png) 84 | 85 | After saving the raw JSONSchema, you can preview your event type to make sure that the schema matches what you expected. 86 | 87 | ## Event Catalog 88 | 89 | After defining your event types and schemas, your users will be able to preview all your event types from the Event Catalog in the [App Portal](../app-portal), or optionally from the [published event catalog](../event-types#publishing-your-event-catalog) if enabled. 90 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: "Svix Docs", 3 | tagline: "Webhooks as a service", 4 | url: "https://docs.svix.com", 5 | baseUrl: "/", 6 | onBrokenLinks: "throw", 7 | onBrokenMarkdownLinks: "throw", 8 | favicon: "img/favicon-96x96.png", 9 | themeConfig: { 10 | image: 'img/socialbanner.png', 11 | navbar: { 12 | logo: { 13 | alt: "Svix logo", 14 | src: "img/brand.svg", 15 | srcDark: "img/brand.white.svg", 16 | }, 17 | items: [ 18 | { 19 | to: "https://www.svix.com", 20 | label: "Website", 21 | position: "left", 22 | }, 23 | { 24 | to: "https://api.svix.com/docs", 25 | label: "API Reference", 26 | position: "left", 27 | }, 28 | { 29 | to: "https://docs.svix.com/receiving/introduction/", 30 | label: "Consuming Webhooks", 31 | position: "left", 32 | }, 33 | { 34 | to: "https://www.svix.com/slack/", 35 | label: "Community", 36 | position: "left", 37 | }, 38 | { 39 | to: "/get-help", 40 | label: "Support", 41 | position: "left", 42 | }, 43 | { 44 | href: 'https://github.com/svix/svix-webhooks', 45 | className: 'header-github-link', 46 | 'aria-label': 'GitHub repository', 47 | position: 'right', 48 | }, 49 | { 50 | to: "https://www.svix.com/signup/", 51 | label: "Get Started", 52 | className: "cta", 53 | position: "left" 54 | } 55 | ], 56 | }, 57 | footer: { 58 | style: "dark", 59 | copyright: `Copyright © Svix`, 60 | }, 61 | prism: { 62 | additionalLanguages: ["ruby", "php", "java", "groovy", "csharp", "rust", "kotlin"], 63 | }, 64 | }, 65 | presets: [ 66 | [ 67 | "@docusaurus/preset-classic", 68 | { 69 | docs: { 70 | routeBasePath: "/", 71 | sidebarPath: require.resolve("./sidebars.js"), 72 | editUrl: "https://github.com/svix/svix-docs/edit/main/", 73 | sidebarCollapsible: false, 74 | }, 75 | blog: false, 76 | theme: { 77 | customCss: require.resolve("./src/css/custom.css"), 78 | }, 79 | gtag: { 80 | trackingID: "G-Z7S16CMH3G", 81 | anonymizeIP: true, 82 | }, 83 | }, 84 | ], 85 | ], 86 | plugins: [ 87 | [ 88 | "@docusaurus/plugin-client-redirects", 89 | { 90 | redirects: [ 91 | { 92 | to: "/receiving/verifying-payloads/why", 93 | from: "/receiving/verifying-payloads", 94 | }, 95 | { 96 | to: "/app-portal", 97 | from: "/management-ui", 98 | }, 99 | { 100 | to: "/retries", 101 | from: "/account/retries" 102 | }, 103 | { 104 | to: "/connectors", 105 | from: "/transformation-templates", 106 | }, 107 | { 108 | to: "/advanced-endpoints/polling-endpoints", 109 | from: "/polling-endpoints", 110 | } 111 | ], 112 | }, 113 | ], 114 | [ 115 | require.resolve('docusaurus-lunr-search'), 116 | { 117 | }, 118 | ], 119 | ], 120 | scripts: [{ 121 | src: "/js/segment.js", 122 | async: true, 123 | defer: true, 124 | } 125 | ], 126 | markdown: { 127 | mdx1Compat: { 128 | comments: false, 129 | admonitions: false, 130 | headingIds: false, 131 | }, 132 | } 133 | }; 134 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svix-docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "dev": "docusaurus start", 9 | "build": "docusaurus build", 10 | "swizzle": "docusaurus swizzle", 11 | "deploy": "docusaurus deploy", 12 | "serve": "docusaurus serve", 13 | "clear": "docusaurus clear" 14 | }, 15 | "dependencies": { 16 | "@docusaurus/core": "3.7", 17 | "@docusaurus/plugin-client-redirects": "3.7", 18 | "@docusaurus/preset-classic": "3.7", 19 | "@mdx-js/react": "^3.1.0", 20 | "clsx": "^2.1.1", 21 | "docusaurus-lunr-search": "^3.6.0", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.5%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | }, 37 | "devDependencies": { 38 | "@docusaurus/module-type-aliases": "3.7", 39 | "@docusaurus/tsconfig": "3.7", 40 | "@types/react": "^19.0.0", 41 | "@types/react-helmet": "^6.1.0", 42 | "@types/react-router-dom": "^5.1.7", 43 | "typescript": "^5.2.2" 44 | }, 45 | "resolutions": { 46 | "loader-utils@npm:^2.0.0": "2.0.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mainSidebar: [ 3 | { 4 | Introduction: ["introduction", "quickstart", "overview", "common-usage-examples", "consuming-webhooks"], 5 | Basics: ["event-types", "app-portal", "api-keys", "documenting-webhooks"], 6 | "Delivery Information": [ 7 | "incoming-webhooks", 8 | "retries", 9 | "opentelemetry-streaming", 10 | ], 11 | "Advanced Functionality": [ 12 | "idempotency", 13 | "rate-limit", 14 | "transformations", 15 | "connectors", 16 | "channels", 17 | { 18 | type: 'category', 19 | label: 'Advanced Endpoint Types', 20 | link: { 21 | type: 'doc', 22 | id: 'advanced-endpoints/intro', 23 | }, 24 | items: [ 25 | 'advanced-endpoints/polling-endpoints', 26 | 'advanced-endpoints/fifo-endpoints', 27 | 'advanced-endpoints/object-storage', 28 | ] 29 | }, 30 | ], 31 | "Security & Compliance": [ 32 | "security", 33 | "retention", 34 | "endpoint-authentication", 35 | "receiving/source-ips", 36 | ], 37 | "Manage Your Account": ["account/environments", "account/org-members"], 38 | }, 39 | { 40 | Tutorials: [ 41 | "tutorials/cli", 42 | "tutorials/api-webhook-management", 43 | "tutorials/event-type-schema", 44 | "tutorials/connectors", 45 | "sending-messages-with-bridge", 46 | ], 47 | }, 48 | { 49 | Integrations: [ 50 | "integrations/zapier", 51 | "integrations/advanced-zapier", 52 | "integrations/ngrok", 53 | ], 54 | }, 55 | { 56 | "Using Ingest": ["receiving/receiving-with-ingest"] 57 | }, 58 | { 59 | "Svix Play": ["play"] 60 | }, 61 | { 62 | "Additional links": [ 63 | "get-help", 64 | { 65 | type: "link", 66 | href: "https://api.svix.com/docs", 67 | label: "API Reference", 68 | }, 69 | ], 70 | }, 71 | ], 72 | consumersSidebar: [ 73 | "receiving/introduction", 74 | { 75 | "Using the App Portal": [ 76 | "receiving/using-app-portal/event-catalog", 77 | "receiving/using-app-portal/adding-endpoints", 78 | "receiving/using-app-portal/testing-events", 79 | "receiving/using-app-portal/filtering-logs", 80 | "receiving/using-app-portal/replaying-messages" 81 | ], 82 | }, 83 | { 84 | "Using Polling Endpoints": [ 85 | "receiving/using-app-portal/polling-endpoints", 86 | ], 87 | }, 88 | { 89 | "Verifying Webhooks": [ 90 | "receiving/verifying-payloads/why", 91 | "receiving/verifying-payloads/how", 92 | "receiving/verifying-payloads/receiving-with-bridge", 93 | "receiving/verifying-payloads/how-manual", 94 | "receiving/additional-authentication", 95 | "receiving/source-ips", 96 | ], 97 | "Additional links": [ 98 | { 99 | type: "ref", 100 | id: "introduction", 101 | label: "Sending Webhooks", 102 | }, 103 | { 104 | type: "ref", 105 | id: "get-help", 106 | }, 107 | ], 108 | }, 109 | ], 110 | }; 111 | -------------------------------------------------------------------------------- /src/components/ConnectorLogo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const svgLogos = { 4 | closecrm: require('../../docs/img/connectors/logos/closecrm-icon.svg'), 5 | customerio: require('../../docs/img/connectors/logos/customerio-icon.svg'), 6 | discord: require('../../docs/img/connectors/logos/discord-icon.svg'), 7 | hubspot: require('../../docs/img/connectors/logos/hubspot-icon.svg'), 8 | loops: require('../../docs/img/connectors/logos/loops-icon.svg'), 9 | 'microsoft-teams': require('../../docs/img/connectors/logos/microsoft-teams-icon.svg'), 10 | resend: require('../../docs/img/connectors/logos/resend-icon.svg'), 11 | sendgrid: require('../../docs/img/connectors/logos/sendgrid-icon.svg'), 12 | segment: require('../../docs/img/connectors/logos/segment-icon.svg'), 13 | slack: require('../../docs/img/connectors/logos/slack-icon.svg'), 14 | windmill: require('../../docs/img/connectors/logos/windmill-icon.svg'), 15 | zapier: require('../../docs/img/connectors/logos/zapier-icon.svg'), 16 | }; 17 | 18 | const pngLogos = { 19 | inngest: require('../../docs/img/connectors/logos/inngest-icon.png').default, 20 | }; 21 | 22 | export default function ConnectorLogo({ name }) { 23 | let Logo = svgLogos[name.toLowerCase()]?.default; 24 | 25 | const src = pngLogos[name.toLowerCase()]; 26 | if (src) { 27 | Logo = () => {name}; 28 | } 29 | 30 | if (!Logo) return null; 31 | 32 | return ( 33 |
42 | 43 |
44 | ); 45 | } -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: rgba(1, 130, 227); 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | 19 | --ifm-menu-link-padding-horizontal: 1rem; 20 | --docusaurus-announcement-bar-height: 0 !important; 21 | } 22 | 23 | .docusaurus-highlight-code-line { 24 | background-color: rgb(72, 77, 91); 25 | display: block; 26 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 27 | padding: 0 var(--ifm-pre-padding); 28 | } 29 | 30 | @media screen and (min-width: 768px) { 31 | main > .container { 32 | padding: 2rem 4rem !important; 33 | } 34 | } 35 | 36 | /* Better sidebar { */ 37 | .menu a:not([href]).menu__link { 38 | margin-top: 1.5rem; 39 | margin-bottom: 0.5rem; 40 | color: black; 41 | font-size: 0.8rem; 42 | font-weight: bold; 43 | letter-spacing: 1px; 44 | text-transform: uppercase; 45 | } 46 | html[data-theme="dark"] .menu a:not([href]).menu__link { 47 | color: grey; 48 | } 49 | /* } */ 50 | 51 | article img { 52 | border-radius: var(--ifm-global-radius); 53 | box-shadow: var(--ifm-global-shadow-lw); 54 | } 55 | 56 | /* Github button { */ 57 | .header-github-link:hover { 58 | opacity:.6 59 | } 60 | .header-github-link:before { 61 | content:""; 62 | padding-left: 0.15em; 63 | padding-right: 0.15em; 64 | width:1.5em; 65 | height:1.5em; 66 | display:flex; 67 | background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat 68 | } 69 | html[data-theme=dark] .header-github-link:before { 70 | background:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat 71 | } 72 | 73 | .cta { 74 | display: inline-flex; 75 | align-items: center; 76 | justify-content: center; 77 | border-radius: 0.375rem; 78 | border-style: solid; 79 | border-color: #2C70FF; 80 | background-color: #2C70FF; 81 | margin-left: 1rem; 82 | color: #ffffff; 83 | } 84 | 85 | .cta:hover { 86 | background-color: #1e4baa; 87 | border-color: #1e4baa; 88 | color: #ffffff; 89 | } 90 | /* } */ 91 | -------------------------------------------------------------------------------- /src/theme/CLITabs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Tabs from '@theme/Tabs'; 4 | 5 | export default function CLITabs({ children }) { 6 | return ( 7 | 18 | 19 | {children} 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/theme/CodeTabs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Tabs from '@theme/Tabs'; 4 | 5 | export default function CodeTabs({ children, additionalTabs=[] }) { 6 | return ( 7 | 24 | 25 | {children} 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/theme/EventTypeTags.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Tabs from '@theme/Tabs'; 4 | 5 | export default function EventTypeTabs({ children, additionalTabs=[] }) { 6 | return ( 7 | 16 | 17 | {children} 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/.nojekyll -------------------------------------------------------------------------------- /static/img/app-portal/add-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/add-endpoint.png -------------------------------------------------------------------------------- /static/img/app-portal/date-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/date-filter.png -------------------------------------------------------------------------------- /static/img/app-portal/embedded-view-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/embedded-view-screen.png -------------------------------------------------------------------------------- /static/img/app-portal/embedded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/embedded.png -------------------------------------------------------------------------------- /static/img/app-portal/endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/endpoint.png -------------------------------------------------------------------------------- /static/img/app-portal/event-catalog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/event-catalog.png -------------------------------------------------------------------------------- /static/img/app-portal/log-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/log-list.png -------------------------------------------------------------------------------- /static/img/app-portal/replay-modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/replay-modal.png -------------------------------------------------------------------------------- /static/img/app-portal/resend-single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/resend-single.png -------------------------------------------------------------------------------- /static/img/app-portal/testing-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/app-portal/testing-endpoint.png -------------------------------------------------------------------------------- /static/img/brand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /static/img/brand.white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /static/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/favicon-16x16.png -------------------------------------------------------------------------------- /static/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/favicon-32x32.png -------------------------------------------------------------------------------- /static/img/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/favicon-96x96.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/ingest/destination-endpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/ingest/destination-endpoints.png -------------------------------------------------------------------------------- /static/img/ingest/destination-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/ingest/destination-logs.png -------------------------------------------------------------------------------- /static/img/ingest/source-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/ingest/source-edit.png -------------------------------------------------------------------------------- /static/img/ingest/url-token-rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/ingest/url-token-rotate.png -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/img/socialbanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svix/svix-docs/dcf9a1586d90a7409cfef7087dec8a1edc4dba7b/static/img/socialbanner.png -------------------------------------------------------------------------------- /static/js/segment.js: -------------------------------------------------------------------------------- 1 | // Load PostHog.js 2 | !function (t, e) { var o, n, p, r; e.__SV || (window.posthog = e, e._i = [], e.init = function (i, s, a) { function g(t, e) { var o = e.split("."); 2 == o.length && (t = t[o[0]], e = o[1]), t[e] = function () { t.push([e].concat(Array.prototype.slice.call(arguments, 0))) } } (p = t.createElement("script")).type = "text/javascript", p.crossOrigin = "anonymous", p.async = !0, p.src = s.api_host + "/static/array.js", (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore(p, r); var u = e; for (void 0 !== a ? u = e[a] = [] : a = "posthog", u.people = u.people || [], u.toString = function (t) { var e = "posthog"; return "posthog" !== a && (e += "." + a), t || (e += " (stub)"), e }, u.people.toString = function () { return u.toString(1) + ".people (stub)" }, o = "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep".split(" "), n = 0; n < o.length; n++)g(u, o[n]); e._i.push([i, s, a]) }, e.__SV = 1) }(document, window.posthog || []); 3 | 4 | // Load Segment.js 5 | !function () { 6 | var i = "analytics", analytics = window[i] = window[i] || []; if (!analytics.initialize) if (analytics.invoked) window.console && console.error && console.error("Segment snippet included twice."); else { 7 | analytics.invoked = !0; analytics.methods = ["trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", "reset", "group", "track", "ready", "alias", "debug", "page", "screen", "once", "off", "on", "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware", "register"]; analytics.factory = function (e) { return function () { if (window[i].initialized) return window[i][e].apply(window[i], arguments); var n = Array.prototype.slice.call(arguments); if (["track", "screen", "alias", "group", "page", "identify"].indexOf(e) > -1) { var c = document.querySelector("link[rel='canonical']"); n.push({ __t: "bpc", c: c && c.getAttribute("href") || void 0, p: location.pathname, u: location.href, s: location.search, t: document.title, r: document.referrer }) } n.unshift(e); analytics.push(n); return analytics } }; for (var n = 0; n < analytics.methods.length; n++) { var key = analytics.methods[n]; analytics[key] = analytics.factory(key) } analytics.load = function (key, n) { var t = document.createElement("script"); t.type = "text/javascript"; t.async = !0; t.setAttribute("data-global-segment-analytics-key", i); t.src = "https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js"; var r = document.getElementsByTagName("script")[0]; r.parentNode.insertBefore(t, r); analytics._loadOptions = n }; analytics._writeKey = "1L0wCkeEIAoXbVTP3rQWvC2G1kMBWQse";; analytics.SNIPPET_VERSION = "5.2.0"; 8 | analytics.load("1L0wCkeEIAoXbVTP3rQWvC2G1kMBWQse"); 9 | 10 | analytics.ready(() => { 11 | window.posthog.init("phc_vqk6r12rFcoSqqRVNIT81inyGF0y1a753NjKzKGaPg", { 12 | api_host: 'https://eu.i.posthog.com', 13 | segment: window.analytics, 14 | capture_pageview: false, 15 | capture_pageleave: true, 16 | 17 | loaded: (posthog) => { 18 | // When the posthog library has loaded, call `analytics.page()` explicitly. 19 | window.analytics.page() 20 | } 21 | }); 22 | }) 23 | } 24 | }(); 25 | -------------------------------------------------------------------------------- /static/webhook-ips.json: -------------------------------------------------------------------------------- 1 | { 2 | "us": [ 3 | "44.228.126.217", 4 | "50.112.21.217", 5 | "52.24.126.164", 6 | "54.148.139.208", 7 | "2600:1f24:64:8000::/56" 8 | ], 9 | "us-east": [ 10 | "54.164.207.221", 11 | "54.90.7.123", 12 | "2600:1f28:37:4000::/56" 13 | ], 14 | "eu": [ 15 | "52.215.16.239", 16 | "54.216.8.72", 17 | "63.33.109.123", 18 | "2a05:d028:17:8000::/56" 19 | ], 20 | "in": [ 21 | "13.126.41.108", 22 | "15.207.218.84", 23 | "65.2.133.31" 24 | ], 25 | "au": [ 26 | "13.239.204.236", 27 | "54.66.246.217", 28 | "54.252.65.96", 29 | "2406:da2c:13:4000::/56" 30 | ], 31 | "ca": [ 32 | "52.60.44.49", 33 | "3.98.68.230", 34 | "3.96.105.27", 35 | "2600:1f21:1c:4000::/56" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@docusaurus/tsconfig", 3 | "include": ["src/"] 4 | } 5 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "cleanUrls": false, 3 | "trailingSlash": false 4 | } 5 | --------------------------------------------------------------------------------