# Inbound Webhooks

Source: https://dev.arcxp.com/integrations/webhooks/ifx-inbound-webhooks/

Inbound Webhooks (Incoming HTTP requests) offers a way for your integration to receive data to a unique URL from third-party integrations. This enables these third parties to automate messages to your integration when something happens in their system. These user-defined callbacks coupled with custom payloads open a wide variety of use cases.

> **Compliance Notice:**
> 
> Do not send personally identifiable information (PII) [through IFX Webhooks](https://dev.arcxp.com/integrations/ifx-data-processing/).

Inbound Webhooks (Incoming HTTP requests) allow your integration to receive data at a unique URL from third-party systems. This lets those systems automatically send messages to your integration whenever something happens on their end. These user-defined callbacks, combined with custom payloads, enable a wide range of automation scenarios.

For example, converting article text to an MP3 audio file (text-to-speech) can take time. You can create a webhook to receive a notification as soon as the MP3 is ready.

To use this feature, start by creating a [custom event](https://dev.arcxp.com/integrations/events/ifx-custom-events/). Custom events are private to your organization and can be used in any way you choose.

This document provides tips and best practices for using Inbound Webhooks as well as additional resources.

## Up first: Local development

Make sure you have your [local environment and integration set up](https://dev.arcxp.com/integrations/manage-integrations/integration-setup/). You can also reference [Creating handlers to receive events with IFX](https://dev.arcxp.com/integrations/manage-integrations/creating-handlers-to-receive-events-with-ifx/).

Webhooks require [a custom event](https://dev.arcxp.com/integrations/events/ifx-custom-events/), but we can start locally before touching sandbox or production.

1. Create a handler to receive the custom event. You can name it whatever you like.  
/src/eventsRouter.js  
```js
{
  "demoHandler": [
    "custom:demo_event",
  ]
}
```
/src/eventsHandlers/demoHandler.js  
```js
const demoHandler = async (event) => {
  console.log("event", event)
}
```
2. Start up the local server  
`npm run localTestingServer`
3. Send a request  
To run and test your custom event integration locally, `key` and `body` are required to invoke your integration. When using the “live” Webhook URL, any JSON payload is accepted.  
`POST http://127.0.0.1:8080/ifx/local/invoke/`  
```json
{
  "key": "custom:demo_event",
  "body": {
    "custom": "some custom data"
  }
}
```
4. Verify the handler was reached by checking the console log in the terminal.

You can choose to deploy the starter bundle right away to test it in action, or finish writing your full integration before deploying to the sandbox. Next, we’ll cover the steps for moving to sandbox.

## Getting to sandbox

All aspects of integration management, including custom events and webhooks, can be managed using IFX’s [CLI commands](https://dev.arcxp.com/integrations/ifx-cli/) or [APIs](https://dev.arcxp.com/api/integrations/integrations-production/).

> **Tip:**
> 
> This same workflow will apply to production when you are ready.

1. From your local machine, [build and zip the bundle](https://dev.arcxp.com/integrations/bundles/bundle-management/#build-a-bundle-locally)
2. [Upload, deploy, promote to sandbox](https://dev.arcxp.com/integrations/bundles/bundle-management/#upload-a-bundle)
3. Create a custom event on sandbox:  
`POST /ifx/api/v1/admin/events/custom`  
```json
{
  "eventName": "custom:demo_event",
  "description": "Here is my description"
}
```
4. Create a webhook that will trigger the event created above:  
`POST /ifx/api/v1/admin/events/custom/custom:demo_event/webhooks`  
```json
{
  "description": "Here is my webhook"
}
```
> **Tip:**  
>  
> Webhooks-to-custom events are one-to-one, so you cannot create a second webhook for the same custom event.
5. Subscribe the integration to your custom event  
`POST /ifx/api/v1/admin/events/subscriptions`  
```json
{
  "eventName": "custom:demo_event",
  "enabled": true,
  "integrationName": "integrationName"
}
```
6. Send a POST request with a custom payload to the webhook URL you received when you created the webhook.  
```json
{
  "test": "testing"
}
```
You must authenticate using a **Restricted Developer Token**. See [How to Create a Restricted-Access Developer Token](https://dev.arcxp.com/api/accessing-arcxp-apis/) to do this. At the bottom of the _Create restricted access token_ page, you will see Platform APIs. Select Full Access for IFX Webhook:  
![Platform APIs](https://dev.arcxp.com/images/ifx/platform-apis.png)
7. Verify expected behavior by viewing logs or other means such as updated Arc XP Native Specification (ANS) data.

## View all webhooks

You can view a list of your webhooks and their associated custom events with `GET /ifx/api/v1/admin/events/custom/webhooks`

## Code example

This example guides you through creating a very simple integration so you can practice and see it in action. Download the source code: <https://github.com/arcxp/ifx-example-webhooks>. The README walks you through development.

## Best practices

> **Compliance Notice:**
> 
> Do not send personally identifiable information (PII) through IFX Webhooks.

### Use one webhook per third-party integration

It is highly recommended to use a single webhook for each third-party integration.

There may (and should) be occasions where you need to regenerate a Personal Access Token (PAT) for a webhook. By using one webhook per third-party integration, you avoid having to update multiple external systems whenever you rotate a token.

Assigning each webhook to a single third-party source also helps:

- **Isolate potential misuse**, minimizing the impact of an exposed or misused token.
- **Enable faster remediation**, making it easier to revoke and replace a token without disrupting other systems.

### Rate limits

Webhooks are subject to a rate limit of 15/s.

### Webhook requests must be authenticated using bearer tokens

A bearer token is sent through a header with a request to the Webhook. If the third party does not support this method of authentication, IFX webhooks cannot be used at this time. Generate a restricted PAT for your webhook: [How to Create a Restricted-Access Developer Token](https://dev.arcxp.com/api/accessing-arcxp-apis/).

### What request types are accepted?

Webhooks can only receive POST requests.

### Webhooks return _only_ an HTTP status code

Webhooks are designed to be asynchronous, fire-and-forget triggers. When a webhook is invoked, the receiving integration processes the event independently and does not send data back in the response body. IFX returns only an HTTP status code e.g., `200 OK` to confirm that the event was delivered to the downstream endpoint.

This design ensures:

- **Reliability and performance** — downstream processing does not block delivery and does not increase latency for other callers.
- **Security and predictability** — avoiding arbitrary response bodies reduces risk of leaking sensitive data and keeps request/response contracts stable.
- **Industry-standard behavior** — leading webhook providers (such as GitHub, Slack, and AWS EventBridge) also expect receivers to respond only with an HTTP status code, not with a custom response body.

For debugging and validation, you can add `console.log` to your handler and see this data in the logs. As always, do not log sensitive data.

### Avoiding Infinite Loops

_Do not use a Webhook to trigger an integration which is listening for the same event._ This will create an infinite loop. In the case you ever do have an infinite loop, you can stop it by [unsubscribing from the event](https://dev.arcxp.com/integrations/events/ifx-events#unsubscribing-from-an-event) that is triggering your integration.

> **Ingestion messages are not supported:**
> 
> Messages with a priority of `ingestion` are not supported, which is commonly used for content migrations or bulk-updating content.  
> 
> If you are subscribed to `story:update` and send updates for that story from your integration back to Draft API, this will re-send `story:update` creating an infinite loop.
> 
> To avoid this it is crucial to include an HTTP header `Arc-Priority: ingestion` so that it will be ignored when it is coming back around.
> 
> ```
> const headers = {
>  'Authorization': `Bearer ${arcPat}`,
>  'Arc-Priority': 'ingestion'
> };
> ```
> 
> If you are using **Migration Center**, please reference [Sending Multiple ANS Objects to Arc XP Services Using Migration Center ](https://dev.arcxp.com/migration-center/developer-docs/sending-multiple-ans-objects-to-arc-xp-services-using-migration-center/).

### Receiving Data

- Webhooks are designed for you to receive notifications with information from third parties and are not meant to support passing of massive amounts of data.
- Can receive any custom payload under 256 KB.
- Are subject to a default rate limit of 15 requests per second. Note that other downstream systems such as Draft API have their own rate limits.

## FAQ

### Does this affect my integration usage count?

Yes, webhook requests do count towards an integration’s usage. Be mindful of if/when to tell the third party to trigger a webhook. For example, do you want every single story update to go from IFX to the third party, or just in certain situations? These decisions will impact the number of requests the third party sends to IFX.

### What about failures or guaranteed delivery of messages to IFX?

Your integration should handle errors properly. For example, using the Node.js language, you should use a “try/catch”, where on catch (error) you can decide what action you would like to take, such as a Slack notification.

---
  
Have a feature request or suggestion? Let us know on [Our Ideas Portal](https://ideas.arcxp.com/?project=IFX).
