> For the complete documentation index, see [llms.txt](https://docs.agilecase.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.agilecase.com/developer-documentation/webhooks/outgoing-webhooks.md).

# Outgoing Webhooks

An outgoing webhook sends an HTTP request to another system, carrying whatever case data you choose. It is triggered from an [on save script](/developer-documentation/case-scripting/on-save-scripts.md), so it fires under conditions you control rather than on every save.

Setting one up takes two pieces: a template that holds the destination and the payload, and a line of script that fires it.

## How it fits together

The template does most of the work. Its **To** field holds the request to make, and its **Body** becomes the payload. The script only names it.

```mermaid
flowchart LR
    Save[Case saved] --> Script["on_save returns true"]
    Script --> Call["Service.SendWebhook('Notify CRM')"]
    Call --> Template["SMS template 'Notify CRM'"]
    Template -->|To field| Url["POST https://..."]
    Template -->|Body, merge fields resolved| Payload[Payload]
    Url --> Send[HTTP request]
    Payload --> Send
    Send --> Record[Communication logged on the case]
```

{% hint style="info" %}
Outgoing webhooks are configured on an **SMS template**, not an email template. This is a quirk of how the feature was built rather than anything to do with SMS, and no text message is sent. Name the template for what the webhook does so it is not mistaken for a real SMS template later.
{% endhint %}

## Setting one up

{% stepper %}
{% step %}

### Create an SMS template

Add a new SMS template and give it a name you will use from your script, for example `Notify CRM`.
{% endstep %}

{% step %}

### Put the request in the To field

The **To** field holds the HTTP method and the URL, separated by a space:

```
POST https://example.com/hooks/agilecase
```

`POST` and `GET` are both supported, over `http` or `https`. Anything other than `POST` is treated as a `GET`.

The URL itself is passed through the case's merge fields, so it can include case data:

```
POST https://example.com/hooks/case/{{Case.CaseReference}}
```

{% endstep %}

{% step %}

### Add any headers you need

Headers go in the same **To** field, each wrapped in double square brackets as a name and value pair:

```
POST https://example.com/hooks/agilecase [[Authorization,Bearer abc123]] [[X-Source,AgileCase]]
```

The header blocks are removed from the text before the URL is read, so they can sit anywhere in the field.
{% endstep %}

{% step %}

### Write the payload in the Body field

The template body is the payload, and it is rendered with the case's merge fields before it is sent. For a `POST`, write the JSON you want to send:

```json
{
  "reference": "{{Case.CaseReference}}",
  "status": "{{Case.Status}}",
  "rating": "{{Case.Custom.CustomerRating}}"
}
```

For a `GET`, the body is appended to the end of the URL instead, so write it as a query string such as `?ref={{Case.CaseReference}}`.
{% endstep %}

{% step %}

### Call it from a script

In an on save script, name the template:

```javascript
[Changed.CustomField("Case Stage").Changed]
function on_save() {
    if (Changed.CustomField("Case Stage").NewValueAsText == 'Completed') {
        Service.SendWebhook('Notify CRM');
    }
    return true;
}
```

Like everything else queued through `Service`, the webhook is only sent if the script returns `true`.
{% endstep %}
{% endstepper %}

## What gets sent

A `POST` sends the rendered body with a content type of `application/json`, encoded as UTF-8. A `GET` sends no body and appends the rendered body text to the URL. Requests are made over TLS 1.2.

Only the fields you put in the template body are sent. There is no default payload, so a webhook with an empty template body sends nothing useful.

## Seeing what happened

Every attempt is recorded against the case as a communication of type webhook, whether it succeeded or not. The record holds the payload that was sent, the HTTP status code that came back, and the response body. It is marked private, so it is not visible to client portal users.

This record is the first place to look when a webhook does not appear to have worked, because it shows exactly what the other system received and said in reply. Responses that are not successful are also written to the error log.

## Limits worth knowing

<table><thead><tr><th width="220">Behaviour</th><th>What it means for you</th></tr></thead><tbody><tr><td>Sent in the background</td><td>The save does not wait for the request, and a slow or unavailable endpoint will not hold up the user.</td></tr><tr><td>No retries</td><td>A request that fails is not tried again. If delivery has to be guaranteed, the receiving system needs to be able to ask AgileCase for the data instead.</td></tr><tr><td>Failures are silent to the user</td><td>Nothing is shown on screen if the endpoint rejects the request. Check the communication record on the case.</td></tr><tr><td>No payload signing</td><td>There is no signature for the receiver to verify. Authenticate with a token in a header, or a secret in the URL, and treat both as credentials.</td></tr><tr><td>The URL cannot contain spaces</td><td>The To field is split on the space between the method and the URL. Encode any spaces that a merge field might introduce.</td></tr></tbody></table>

{% hint style="warning" %}
If the To field does not contain a method and URL in the expected form, nothing is sent and no error is raised. A webhook that appears to do nothing at all is usually a To field that AgileCase could not read, so check it begins with `POST` or `GET` followed by a space and a full `https://` URL.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.agilecase.com/developer-documentation/webhooks/outgoing-webhooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
