> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ComposioHQ/agent-orchestrator/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Notifier

> Send notifications to any HTTP endpoint with retry logic

The Webhook notifier plugin sends JSON-formatted notifications to any HTTP endpoint. It includes automatic retry logic with exponential backoff and supports custom headers for authentication.

## Overview

The Webhook notifier provides:

* Generic HTTP POST to any endpoint
* Structured JSON payload format
* Automatic retry with exponential backoff
* Custom header support (authentication, etc.)
* Smart retry logic (only retries 429 and 5xx errors)
* Configurable retry behavior

<Info>
  This is the most flexible notifier plugin. Use it to integrate with any service that accepts HTTP webhooks: Discord, Microsoft Teams, custom dashboards, alerting systems, etc.
</Info>

## Configuration

Add the webhook notifier to your `agent-orchestrator.yaml`:

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://your-endpoint.com/webhook
      headers:                    # Optional
        Authorization: Bearer ${API_TOKEN}
        X-Custom-Header: value
      retries: 2                  # Optional
      retryDelayMs: 1000          # Optional
```

### Configuration Options

<ParamField path="url" type="string" required>
  The HTTP endpoint to POST notifications to. Must be a valid HTTP/HTTPS URL.

  <Warning>
    The URL is validated at plugin creation. Invalid URLs will cause the plugin to fail initialization.
  </Warning>
</ParamField>

<ParamField path="headers" type="object" optional>
  Custom HTTP headers to include with every request. Useful for:

  * Authentication: `Authorization: Bearer <token>`
  * API keys: `X-API-Key: <key>`
  * Custom routing: `X-Environment: production`
  * Content hints: `X-Webhook-Source: agent-orchestrator`

  Only string values are supported. Non-string values are ignored.
</ParamField>

<ParamField path="retries" type="number" default={2}>
  Maximum number of retry attempts for failed requests. Set to `0` to disable retries.

  **Retry behavior:**

  * Only retries 429 (Too Many Requests) and 5xx (server errors)
  * 4xx errors (400, 401, 403, 404) are NOT retried (client errors are permanent)
  * Network errors are retried
</ParamField>

<ParamField path="retryDelayMs" type="number" default={1000}>
  Initial delay in milliseconds before first retry. Uses exponential backoff:

  * Attempt 1: `retryDelayMs * 2^0` = 1000ms
  * Attempt 2: `retryDelayMs * 2^1` = 2000ms
  * Attempt 3: `retryDelayMs * 2^2` = 4000ms

  Must be non-negative. Invalid values default to 1000ms.
</ParamField>

## Payload Format

The webhook plugin sends JSON payloads with different structures based on the notification type.

### Standard Notification

Sent via `notify()` method:

```json theme={null}
{
  "type": "notification",
  "event": {
    "id": "evt_123",
    "type": "pr.created",
    "priority": "urgent",
    "sessionId": "session-abc-123",
    "projectId": "my-app",
    "timestamp": "2026-01-15T10:30:00.000Z",
    "message": "Pull request created successfully",
    "data": {
      "prUrl": "https://github.com/owner/repo/pull/123",
      "ciStatus": "passing"
    }
  }
}
```

### Notification with Actions

Sent via `notifyWithActions()` method:

```json theme={null}
{
  "type": "notification_with_actions",
  "event": { /* same as above */ },
  "actions": [
    {
      "label": "Approve",
      "url": "https://github.com/owner/repo/pull/123"
    },
    {
      "label": "Request Changes",
      "callbackEndpoint": "https://orchestrator.example.com/callback/request-changes"
    }
  ]
}
```

### Custom Message

Sent via `post()` method:

```json theme={null}
{
  "type": "message",
  "message": "Custom notification text",
  "context": {
    "channel": "#custom-channel"
  }
}
```

## Usage Examples

### Discord Webhook

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://discord.com/api/webhooks/YOUR/WEBHOOK/ID
      # Discord webhooks require transforming the payload format
```

<Note>
  Discord's webhook format differs from the plugin's default. You may need to use a transformation proxy or build a Discord-specific notifier plugin.
</Note>

### Microsoft Teams

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://outlook.office.com/webhook/YOUR/WEBHOOK/URL
      retries: 3
      retryDelayMs: 2000
```

### Custom Dashboard

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://dashboard.example.com/api/notifications
      headers:
        Authorization: Bearer ${DASHBOARD_API_TOKEN}
        X-Source: agent-orchestrator
        X-Environment: production
```

### Alerting System (PagerDuty, etc.)

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://events.pagerduty.com/v2/enqueue
      headers:
        Authorization: Token token=${PAGERDUTY_TOKEN}
      retries: 5
      retryDelayMs: 500
```

### No Retries (Fire and Forget)

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://analytics.example.com/events
      retries: 0  # Don't retry failed requests
```

## Retry Logic

### What Gets Retried

<AccordionGroup>
  <Accordion title="✅ Retryable Errors">
    * **429 Too Many Requests** - Rate limit, back off and retry
    * **500 Internal Server Error** - Temporary server issue
    * **502 Bad Gateway** - Upstream server issue
    * **503 Service Unavailable** - Server overloaded
    * **504 Gateway Timeout** - Upstream timeout
    * **Network errors** - Connection failures, DNS issues
  </Accordion>

  <Accordion title="❌ Non-Retryable Errors">
    * **400 Bad Request** - Invalid payload format
    * **401 Unauthorized** - Missing/invalid authentication
    * **403 Forbidden** - Insufficient permissions
    * **404 Not Found** - Endpoint doesn't exist
    * **422 Unprocessable Entity** - Invalid data
    * **Other 4xx errors** - Client errors (permanent)
  </Accordion>
</AccordionGroup>

### Exponential Backoff

Retries use exponential backoff to avoid overwhelming the server:

```
Attempt 0: Immediate request
Attempt 1: Wait 1000ms  (retryDelayMs * 2^0)
Attempt 2: Wait 2000ms  (retryDelayMs * 2^1)
Attempt 3: Wait 4000ms  (retryDelayMs * 2^2)
```

<Info>
  Exponential backoff helps prevent cascading failures and respects rate limits on the receiving endpoint.
</Info>

## Error Handling

When all retries are exhausted, the plugin throws an error:

```
Webhook POST failed (503): Service temporarily unavailable
```

The orchestrator's reaction system can handle webhook failures:

```yaml theme={null}
reactions:
  - event: notification.failed
    action: log
    config:
      message: "Failed to send webhook notification after retries"
```

## Security Best Practices

<Warning>
  **Security Checklist:**

  * ✅ Use HTTPS endpoints only
  * ✅ Store sensitive tokens in environment variables
  * ✅ Validate receiving endpoint accepts your payload format
  * ✅ Use authentication headers when available
  * ✅ Rotate API tokens regularly
  * ✅ Monitor for failed webhooks (401/403 errors)
  * ✅ Different credentials per environment
  * ❌ Never commit API tokens to git
  * ❌ Don't use unencrypted HTTP for sensitive data
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="400 Bad Request errors">
    The receiving endpoint rejected your payload format. Check:

    1. Does the endpoint expect the plugin's JSON structure?
    2. Review their webhook documentation
    3. Test with curl:

    ```bash theme={null}
    curl -X POST -H 'Content-Type: application/json' \
      -d '{"type":"notification","event":{...}}' \
      YOUR_WEBHOOK_URL
    ```

    4. Consider building a transformation proxy or custom notifier plugin
  </Accordion>

  <Accordion title="401/403 Authentication errors">
    Authentication failed. Check:

    1. Headers are configured correctly
    2. Token/API key is valid and not expired
    3. Token has correct permissions
    4. Environment variables are loaded properly

    Test with curl:

    ```bash theme={null}
    curl -X POST \
      -H 'Authorization: Bearer YOUR_TOKEN' \
      -H 'Content-Type: application/json' \
      -d '{"test":true}' \
      YOUR_WEBHOOK_URL
    ```
  </Accordion>

  <Accordion title="Timeout or network errors">
    Connection issues:

    1. Verify the URL is accessible from your network
    2. Check firewall rules
    3. Test DNS resolution: `nslookup your-endpoint.com`
    4. Test connectivity: `curl -v YOUR_WEBHOOK_URL`
    5. Increase `retries` and `retryDelayMs` for flaky networks
  </Accordion>

  <Accordion title="No notifications being sent">
    Check logs for:

    ```
    [notifier-webhook] No url configured — notifications will be no-ops
    ```

    Ensure `url` is set in config and validated successfully.
  </Accordion>

  <Accordion title="Retries exhausted quickly">
    If retries are being exhausted:

    1. Check if errors are 4xx (not retryable)
    2. Increase `retries` count
    3. Increase `retryDelayMs` for better spacing
    4. Monitor server logs on receiving end
  </Accordion>
</AccordionGroup>

## Building a Transformation Proxy

If your webhook endpoint expects a different payload format, create a transformation proxy:

```typescript theme={null}
// proxy.ts - Transform to Discord format
app.post('/webhook/discord', async (req, res) => {
  const { type, event, message } = req.body;
  
  // Transform to Discord webhook format
  const discordPayload = {
    content: message || event?.message,
    embeds: event ? [{
      title: `${event.type} — ${event.sessionId}`,
      description: event.message,
      color: event.priority === 'urgent' ? 0xff0000 : 0x0099ff,
      timestamp: event.timestamp
    }] : []
  };
  
  await fetch(process.env.DISCORD_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(discordPayload)
  });
  
  res.status(200).send('OK');
});
```

Then configure the webhook plugin to use your proxy:

```yaml theme={null}
plugins:
  notifier:
    name: webhook
    config:
      url: https://your-proxy.com/webhook/discord
```

## Source Code

View the plugin source:

* Package: `@composio/ao-plugin-notifier-webhook`
* Location: `packages/plugins/notifier-webhook/src/index.ts`

## Related

* [Slack Notifier](/plugins/notifier/slack) - Slack-specific webhook implementation
* [Desktop Notifier](/plugins/notifier/desktop) - OS-native notifications
* [Composio Notifier](/plugins/notifier/composio) - Unified notifications via Composio
* [Building Custom Plugins](/plugins/creating-plugins) - Create your own notifier plugin


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.