> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Send web request Action

> [!TIP]
>
> This page describes the "Send web request" action. To start a workflow with a web request, use the ["Web request received" trigger](https://primer.io/docs/workflows/apps/native-apps/primer-web-requests-app/web-request-received-trigger.md).

The "Send web request" action can be used to send a web request to an external server. This could be your own endpoint or the endpoint of another service accepting web requests.

## Configuring the app

Credentials for outbound requests are configured once on the Primer Web Requests app, not on each action. When you connect the app you can set:

| Setting | Description |
| :- | :- |
| Authorization Header | The credential sent with every request made by the "Send web request" action. By default it is sent as the `Authorization` header. Leave it empty if your endpoint does not require authentication. |
| Custom Authorization Header Name | Overrides the name of the header the credential is sent as, for example `X-API-Key`. If left empty, `Authorization` is used. |
| Incoming Authorization Token | Only used by the ["Web request received" trigger](https://primer.io/docs/workflows/apps/native-apps/primer-web-requests-app/web-request-received-trigger.md#securing-the-webhook) to authenticate requests sent to your webhook URL. |

Keep credentials on the app rather than in a custom header: values on the app are stored with the app connection, whereas custom header values are part of the workflow definition and visible to anyone who can edit the workflow. Connect the app once per set of credentials; you can connect it several times if different workflows need different credentials.

## Restrictions

The action currently has the following restrictions:

* requests are synchronous: the workflow waits for the response before continuing
* only **https** endpoints are supported
* only `GET` and `POST` methods are supported
* only JSON and form URL encoded (`application/x-www-form-urlencoded`) request bodies are supported. A JSON payload must be a JSON object
* custom headers are supported, except for a set of [blocked headers](#blocked-headers) that are dropped from the request
* requests to private or internal network addresses, `localhost`, and any `primer.io` domain are rejected
* the request is sent exactly once; failed requests are not retried
* the response body is only available to later blocks if it is a JSON object

If you have different requirements for your particular automation, please reach out to us directly via our [Support Portal](https://portal.support.primer.io/servicedesk/customer/portal/11) to tell us more about your request. If you don't have access, please contact your account administrator for assistance.

## Sending the request

Configure the following inputs to set up the action:

| Input | Description |
| :- | :- |
| Target URL | The external server to send the request to. Only **https** endpoints are supported. The hostname must resolve to a public IP address; see [URL validation](#url-validation). |
| Method | `POST` (default) or `GET`. No payload is sent for `GET` requests. |
| Body format | `JSON` (default) or `Form URL encoded`. Only available for `POST` requests. |
| JSON payload | Shown when **Body format** is `JSON`. The JSON payload for `POST` requests. It must be a JSON object. There is only **partial validation** available when a proper JSON payload is constructed, as the values are replaced only during runtime of the workflow. If the payload is not valid JSON at runtime, the request is not sent and the action fails. Use the [Workflow Runs](https://primer.io/docs/workflows/monitor-workflows/overview.md) to debug the actual request passed and response received in case of problems. |
| Form fields | Shown when **Body format** is `Form URL encoded`. One entry per field, each with a name and a value. Values can use variables from earlier blocks. See [Sending a form URL encoded body](#sending-a-form-url-encoded-body). |
| Custom Headers | Additional headers to send as `name`/`value` pairs. These can override the default `Content-Type` and `User-Agent` headers. [Blocked headers](#blocked-headers) are dropped. Do not use this for API keys or other credentials; set those on the app instead (see [Configuring the app](#configuring-the-app)). |
| Fail action on unsuccessful request | Boolean (default: **True**). Determines how the workflow handles failed web requests. When **True**, the workflow stops on failure; when **False**, it continues execution even if the request fails. See [Failure handling](#failure-handling). |

Example JSON request with a placeholder for some variables. Use the [variable selector](https://primer.io/docs/get-started/create-workflow.md#add-actions) to assign outputs from different blocks when creating this request.

```json JSON theme={"dark"}
{
  "customRequestType": "RECOMMENDATION_ENGINE",
  "paymentId": "[PAYMENT_ID]",
  "orderId": "[ORDER_ID]",
  "customerId": "[CUSTOMER_ID]",
  "ruleDescription": "Get recommendation on how to proceed after a payment was created."
}
```

### Sending a form URL encoded body

Some APIs expect `application/x-www-form-urlencoded` instead of JSON. Set **Body format** to `Form URL encoded` and add one entry per field under **Form fields**. Brackets in field names are kept, so you can build nested values, for example `items[0][price]`. Names and values are URL encoded when the request is sent.

For example, these fields:

| Name | Value |
| :- | :- |
| `amount` | `1000` |
| `expand[]` | `customer` |
| `expand[]` | `latest_charge` |

are sent as:

```text Request body theme={"dark"}
amount=1000&expand%5B%5D=customer&expand%5B%5D=latest_charge
```

The following rules apply to form fields:

* a field with an empty name is skipped
* a field with an empty value is sent as `name=`
* the same name can appear more than once, for example `expand[]`. Fields are sent in the order of the list

### Headers sent with the request

Every request includes the following headers unless noted otherwise. Header names are case-insensitive, so a custom header with the same name as a default replaces it.

| Header | Value | Notes |
| :- | :- | :- |
| `Content-Type` | `application/json`, or `application/x-www-form-urlencoded` when **Body format** is `Form URL encoded` | Can be overridden with a custom header. The body is not converted to match. |
| `User-Agent` | `Primer Web Request App` | Can be overridden with a custom header. |
| `Authorization` | The **Authorization Header** value set on the app | Only sent if a value is configured on the app. The header name follows **Custom Authorization Header Name** when set. The app value takes precedence over a custom header with the same name. |
| `Referer` | `primer.io` | Always sent and cannot be overridden. It identifies requests coming from Primer and lets the trigger reject requests sent back to Primer. |

Tracing headers used internally by Primer are not forwarded to your endpoint.

### Blocked headers

For security reasons, some headers cannot be set through **Custom Headers**. If a blocked header is configured, it is dropped and the request is still sent. The Workflow Run shows a message listing the headers that were dropped:

```text Workflow Run message theme={"dark"}
The following headers were configured on the workflow, but are not supported: host, x-forwarded-for
```

The following headers are blocked (case-insensitive):

| Category | Headers |
| :- | :- |
| Host and routing | `Host`, `X-Original-URL`, `X-Rewrite-URL` |
| HTTP protocol control | `Content-Length`, `Transfer-Encoding`, `Connection`, `Upgrade`, `Expect` |
| Client IP and proxy identity | `X-Forwarded-For`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Real-IP`, `X-Client-IP`, `X-Original-IP`, `Forwarded`, `Via` |
| Request origin | `Origin`, `Referer` |
| Proxy authentication | `Proxy-Authorization`, `Proxy-Authenticate`, `Proxy-Connection` |
| Primer internal | `X-Primer-Branch`, `X-Primer-Context`, `X-Primer-Flags`, `X-Primer-Timeout-Policy` |
| CDN | Any header starting with `CloudFront-` |

### URL validation

The **Target URL** is validated before the request is sent. The action fails without sending anything if:

* the URL does not use the `https` scheme
* the host is `localhost`, a private or internal IP address (such as `10.x.x.x`, `172.16.x.x` to `172.31.x.x`, `192.168.x.x`, `127.x.x.x`, link-local or other reserved ranges, or their IPv6 equivalents), or a hostname that resolves to one of these addresses
* the hostname cannot be resolved
* the host is `primer.io` or any subdomain of it. This prevents workflows from calling Primer and starting themselves again in a loop.

The error shown in the Workflow Run lists the specific issue, for example `Invalid URL, scheme not allowed: http`.

## Handling the response

The following response data is available in the output of the action:

| Output | Description |
| :- | :- |
| Response Body | The payload, available as "unstructured data" if the response is a JSON object. If the response is not JSON, or is a JSON array or scalar, this is empty. |
| Response content-type | The content type of the response (e.g., `application/json`). |
| Response status | The HTTP status code of the response. |
| Success | Boolean indicating whether the request was successful (`2xx` status). |

You can pass this data to another workflow block using the variable selector. For the **Response Body**, you must specify the **path** to the variable you want to use.

![Condition example](https://mintcdn.com/primer-cc826789/8xC8Qx3j-Ljaxxyf/images/workflows/send-web-request/web-request-app.png?fit=max\&auto=format\&n=8xC8Qx3j-Ljaxxyf\&q=85\&s=e36ff8e71d9a6754265058681822684e)

Condition example

It is also possible to use that data to build conditions in the same way.

![Condition example](https://mintcdn.com/primer-cc826789/8xC8Qx3j-Ljaxxyf/images/workflows/send-web-request/web-request-condition.png?fit=max\&auto=format\&n=8xC8Qx3j-Ljaxxyf\&q=85\&s=2745d423f66f581f04c75bd13237754d)

Condition example

The full request and response, including headers, are shown in the **Request & response** tab of the Workflow Run. Values of credential headers such as `Authorization`, `Cookie`, or headers whose names end in `-Key`, `-Token`, `-Auth` or `-Secret` are masked there.

### Timeouts

| Phase | Limit | Description |
| :- | :- | :- |
| Connecting | 5 seconds | Time allowed to resolve the hostname and open a TCP connection, and then again for the TLS handshake. An endpoint that does not accept connections fails within roughly 10 seconds. |
| Waiting for a response | 60 seconds | Time allowed for the server to start responding and for each subsequent read of the response. A connected endpoint that never responds fails after 60 seconds. |

The request is sent exactly once. If it times out or fails, it is **not retried**, so your endpoint will not receive duplicate requests from a single action execution. If you need retries, add them to your workflow explicitly or handle them on your side.

> [!NOTE]
>
> Keep your endpoint's response time well below the limits above. When this action runs as part of a payment flow, for example before authorizing a payment, the checkout waits for the workflow to finish and a slow endpoint delays the payment result for your customer.

If a use case requires a longer duration, then this synchronous action is not the right approach. Consider splitting the request and using the ["Web request received" trigger](https://primer.io/docs/workflows/apps/native-apps/primer-web-requests-app/web-request-received-trigger.md) to continue.

### Failure handling

A request is treated as unsuccessful when:

* the **Target URL** fails [validation](#url-validation) or, when **Body format** is `JSON`, the **JSON payload** is not valid JSON
* the connection fails or times out, or the server does not respond in time
* the server responds with a status code outside the `2xx` range

By default, an unsuccessful request marks the workflow run as `Failed` and no further blocks are executed. The Workflow Run shows the reason, for example `Unexpected http response code [503] from https://example.com/recommend`.

Set **Fail action on unsuccessful request** to **False** to let the workflow continue instead. The action then completes with **Success** set to `false`. When a response was received, **Response status**, **Response content-type** and **Response Body** are still populated so you can branch on them. When no response was received, for example on a timeout or an invalid URL, only **Success** is set and the Workflow Run shows a message explaining why, such as `the target server took too long to respond`.

![Handling unsuccessful request example](https://mintcdn.com/primer-cc826789/8xC8Qx3j-Ljaxxyf/images/workflows/send-web-request/web-request-fail-action-on-unsuccessful-request.png?fit=max\&auto=format\&n=8xC8Qx3j-Ljaxxyf\&q=85\&s=a9dc02ec86b92c965a539eb18587b416)

Handling unsuccessful request example

Learn more about [debugging workflow runs](https://primer.io/docs/workflows/monitor-workflows/overview.md).

## Example flow

An example use of the send web request action could be to use an internal recommendation engine before authorizing a payment. If the response contains a "recommendation" field with a value "PROCEED", then the payment flow continues, otherwise the payment gets declined.

![Send web request action example](https://mintcdn.com/primer-cc826789/8xC8Qx3j-Ljaxxyf/images/workflows/send-web-request/internal-lookup-flow.png?fit=max\&auto=format\&n=8xC8Qx3j-Ljaxxyf\&q=85\&s=1c3a93dbf0b48e6640c4311688e2860a)

Send web request action example

## Start automating

* Now that you know what can be achieved with this action, start [creating a workflow](https://primer.io/docs/get-started/create-workflow.md).
* Debug received requests or requests sent within the [Workflow Runs](https://primer.io/docs/workflows/monitor-workflows/overview.md)


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