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

# OutboundAPI connection

> Have an orchestration flow call an HTTP endpoint in your system or a third-party platform when a customer event occurs.

The OutboundAPI block lets a Lobyco orchestration flow call an HTTP endpoint in your system — or any third-party platform such as Salesforce, Bloomreach, Iterable, or Adobe — whenever a customer event occurs. Use it to sync loyalty events into your CRM, trigger journeys in an external marketing platform, or notify your own backend in real time.

This guide describes the technical contract: how the request is built, how authentication works, what your endpoint must do, and the platform's retry, timeout, and security behavior.

<Info>
  **Availability:** the OutboundAPI block is enabled per environment. Contact your Lobyco representative if you don't see it in the orchestration canvas.
</Info>

The inbound counterpart is the [InboundAPI integration](/integration/external-integrations/inboundapi-integration). For the payload schemas Lobyco sends, see [Events](/integration/external-integrations/events).

## When the block fires

The OutboundAPI block runs each time a customer reaches it in an orchestration flow. It must sit **downstream of an event block** — that event supplies the data the block sends. The flow itself can be segmentation-based or event-driven (for example `Segment → Challenge → [event] → OutboundAPI`); the only requirement is that an event block comes somewhere before the OutboundAPI block.

The triggering event can be any of a growing set of platform and activity events — currently **including** customer **sign-up**, **game won / lost**, **challenge completed**, and **offer assigned**. More event types are added over time, so treat this as a non-exhaustive list.

One event for one customer produces one HTTP request. A failed request affects only that customer's journey — other customers in the flow continue normally.

The call is **one-way**: the response from your endpoint is recorded for troubleshooting but does not change the orchestration flow.

## How it works

A marketer (or you, as an integrator) places the OutboundAPI block in an orchestration flow on the visual canvas, downstream of an event block. Each time a customer passes through the block, the platform:

<Steps>
  <Step>
    ### Takes the event data

    Takes the **event data** produced by the upstream event block (for example a sign-up or purchase event).
  </Step>

  <Step>
    ### Enriches the data

    **Enriches** the data with the full customer profile and/or purchase details — but only if your templates reference them (see [Dynamic values](#dynamic-values-placeholders)).
  </Step>

  <Step>
    ### Renders the request

    **Renders** the URL, header values, and body, replacing all placeholders with real data.
  </Step>

  <Step>
    ### Authenticates

    **Authenticates** — obtains an OAuth2 Bearer token, builds a Basic Auth header, or attaches your custom headers.
  </Step>

  <Step>
    ### Sends the request

    **Sends** the HTTP request to your endpoint.
  </Step>

  <Step>
    ### Records the outcome

    Records the outcome (status code, response body, response time) in the run details.
  </Step>
</Steps>

If any of the enrichment, rendering, or authentication steps fails — missing data, an unresolvable placeholder, a failed token request — the block fails **without** calling your endpoint.

<Warning>
  **Simulation runs are real.** When an orchestration flow is run in simulation mode, the OutboundAPI block still sends the HTTP request. Point test flows at a test endpoint.
</Warning>

## Configuration reference

| Setting            | Required | Description                                                                                                                                                                                                                                                                                        |
| ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **URL**            | Yes      | Absolute URL of the endpoint to call. May contain placeholders (e.g. `https://api.example.com/contacts/{{ member.id }}`). Must not resolve to a private network address.                                                                                                                           |
| **HTTP method**    | Yes      | One of GET, POST, PUT, DELETE, PATCH.                                                                                                                                                                                                                                                              |
| **Headers**        | No       | Key–value pairs added to the request. Values may contain placeholders.                                                                                                                                                                                                                             |
| **Body**           | No       | A JSON body template; may contain placeholders. Available for POST, PUT, and PATCH. Always sent with Content-Type: application/json; charset=utf-8 — the content type is fixed, and a Content-Type entry among custom headers is ignored. The configuration UI requires the body to be valid JSON. |
| **Authentication** | No       | Either a saved **Integration** (recommended) or an inline auth configuration. You cannot set both at once.                                                                                                                                                                                         |

The configuration is validated when the flow is saved: the URL and method are checked, an inline auth config must be complete, and a referenced integration must exist and not be deleted.

### Testing the block

The block's configuration dialog has a **Test** button that sends a single real request with the current configuration — including authentication — and shows the response status code, response time, and response body. Use it to confirm credentials and payload format with your endpoint before publishing the flow, and prefer a test endpoint or pre-production environment while doing so.

## Dynamic values (placeholders)

URL, header values, and the body are [Liquid](https://shopify.github.io/liquid/) templates. Placeholders use double curly braces:

```liquid theme={"system"}
{{ source.property }}
```

### Data sources

The placeholder namespace for a customer is called `member`, as are the API and the `memberId` field behind it.

| Source   | Where it comes from                                                                                                                                                                              | Requirement                                                                                                          |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| event    | The payload of the event that triggered the flow. Fields depend on the event type (e.g. \{\{ event.memberId }}, \{\{ event.storeId }}).                                                          | Always available.                                                                                                    |
| member   | The full customer profile, fetched from the Lobyco Member API at execution time. Includes `metadata` — useful for IDs your system already knows, e.g. \{\{ member.metadata.externalContactId }}. | The event payload must contain memberId.                                                                             |
| purchase | The purchase/receipt details, fetched from the Lobyco Purchase API at execution time.                                                                                                            | The event payload must contain completedByPurchaseId — currently only the **Challenge completed** event provides it. |

Enrichment is automatic and on-demand: the platform scans your templates and only calls the Member or Purchase API when a template actually references `member.` or `purchase.`. If the required ID is missing from the event, or the lookup returns no data, the block fails and no request is sent.

<Info>
  **Tip:** in the configuration dialog, type two opening braces to open an autocomplete dropdown of the placeholders available for your flow's event type, including nested values (e.g. `purchase.products[0].name`).
</Info>

### Example body

```json theme={"system"}
{
    "receiptId": "{{ purchase.receiptId }}",
    "totalAmount": {{ purchase.totalAmount }},
    "storeId": "{{ purchase.storeId }}",
    "memberEmail": "{{ member.email }}"
}
```

### Supported Liquid features

| Feature                 | Example                                                   |
| ----------------------- | --------------------------------------------------------- |
| Property access, nested | \{\{ event.user.profile.name }}                           |
| Array indexing          | \{\{ event.items\[0] }}                                   |
| Standard filters        | \{\{ event.name \| upcase }}, \{\{ event.items \| size }} |

Conditionals and loops are supported too:

```liquid theme={"system"}
{% if event.amount > 100 %}high value{% else %}standard{% endif %}

{% for item in event.items %}{{ item.name }}{% endfor %}
```

Loops are capped at 10,000 iterations.

In addition to standard Liquid filters, these filters are available:

| Filter        | Example                                    | Result                                                                                               |
| ------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| at\_least     | \{\{ event.amount \| at\_least: 0 }}       | Lower-bounds a number.                                                                               |
| at\_most      | \{\{ event.amount \| at\_most: 100 }}      | Upper-bounds a number.                                                                               |
| url\_encode   | \{\{ event.query \| url\_encode }}         | Percent-encodes a string for safe use in URLs.                                                       |
| url\_decode   | \{\{ event.encoded \| url\_decode }}       | Decodes a percent-encoded string.                                                                    |
| sort\_natural | \{\{ event.tags \| sort\_natural }}        | Case-insensitive sort of a list.                                                                     |
| date          | \{\{ 'now' \| date: '%Y-%m-%dT%H:%M:%S' }} | Formats a date using strftime patterns. Accepts date values, date strings, 'now', and 'today' (UTC). |

## Authentication

Three authentication types are supported. Credentials can be stored once as a reusable **Integration** in the Lobyco Admin Portal and referenced from any number of flows — this is the recommended approach. Integration secrets are encrypted at rest (AES-256-GCM), always masked in the UI and API responses, and can be rotated in one place. The Admin Portal also offers a **test** action that performs a real call with the configured credentials.

### OAuth2 Client Credentials

For platforms that issue short-lived Bearer tokens (e.g. Salesforce).

| Field                                                   | Required | Description                                                                      |
| ------------------------------------------------------- | -------- | -------------------------------------------------------------------------------- |
| Token URL                                               | Yes      | The token endpoint. Must be publicly reachable (private addresses are rejected). |
| Client ID / Client Secret                               | Yes      | Your OAuth2 client credentials.                                                  |
| Scopes                                                  | No       | Requested scopes, sent space-separated in the token request.                     |
| Additional headers / body parameters / query parameters | No       | Extra values some providers require on the token request.                        |

Token request behavior — your identity provider receives:

```
POST {tokenUrl}
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=...&client_secret=...&scope=...
```

and must return JSON containing `access_token` (required) and `expires_in` (in seconds). The platform then calls your endpoint with `Authorization: Bearer {access_token}`.

Tokens are **cached** for 90% of `expires_in` and reused across executions with the same credentials, so your token endpoint is not called on every event. If `expires_in` is missing or zero, the token is not cached and is requested per call.

### Basic Auth

For platforms authenticating with username/password or key/secret pairs (e.g. Bloomreach private API groups). The platform base64-encodes `username:password` and sends `Authorization: Basic {encoded}` on every request.

### Custom Headers

For platforms using static API-key headers (e.g. Iterable `Api-Key`, Adobe `X-Api-Key`). Configure one or more headers; mark sensitive values as **secret** so they are encrypted and masked. All configured headers are attached to every request.

## What your endpoint must do

* **Respond within 5 seconds.** The per-attempt timeout is 5 seconds by default (configurable per environment). Accept the request, respond, and process asynchronously if your handling is slow.
* **Return a 2xx status for success.** Any 2xx counts as success; everything else marks the block execution as failed. Redirects are followed by standard HTTP client behavior; auth headers are set on the initial request.
* **Be idempotent.** Failed attempts are retried (see below), so the same event can reach your endpoint more than once. The platform does not send an idempotency key today — deduplicate using an identifier from the event payload (e.g. a receipt or activity ID).
* **Expect UTF-8 JSON** when a body is configured (`Content-Type: application/json; charset=utf-8`).

### Retries and timeouts

| Parameter           | Default                                   |
| ------------------- | ----------------------------------------- |
| Timeout per attempt | 5,000 ms                                  |
| Retries             | 2 (up to 3 attempts total)                |
| Backoff             | Jittered exponential, \~100 ms base delay |

Retries trigger on transient failures only: HTTP `5xx`, HTTP `408`, network errors, and timeouts. Other non-success statuses (e.g. `400`, `401`, `404`) are **not** retried and fail the block immediately. The same retry policy applies to OAuth2 token requests.

Defaults can be tuned per environment by Lobyco.

### What is recorded

For every execution, the platform records the response status code, response body, and response time in the run details. This data is visible to flow operators for troubleshooting, so avoid returning sensitive data in your response body.

## Security and restrictions

* **Private networks are blocked.** The target URL — and the OAuth2 token URL — must not resolve (directly or via DNS) to a private or link-local address (`127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, plus Lobyco-internal ranges). This is enforced both when the flow is saved and again at execution time, after placeholders are resolved.
* **Allowed methods** are limited to `GET`, `POST`, `PUT`, `DELETE`, `PATCH`.
* **Body content type** is always `application/json; charset=utf-8`. Other formats (form-urlencoded, XML, multipart) cannot be configured, and a `Content-Type` custom header is silently ignored.
* **Secrets** (OAuth2 client secrets, Basic Auth passwords, secret custom headers) are encrypted at rest and never returned in plaintext by any Lobyco API or UI.

## Example setups

### Salesforce (OAuth2 Client Credentials)

<Steps>
  <Step>
    ### Create a Connected App

    In Salesforce, create a Connected App with the client-credentials flow enabled and note the consumer key/secret.
  </Step>

  <Step>
    ### Create an Integration

    In the Lobyco Admin Portal, create an **Integration** of type **OAuth2 Client Credentials** with Token URL `https://{your-domain}.my.salesforce.com/services/oauth2/token`, the consumer key as Client ID, and the consumer secret as Client Secret.
  </Step>

  <Step>
    ### Configure the OutboundAPI block

    In the OutboundAPI block, reference the integration and configure the target REST endpoint, e.g. `POST https://{your-domain}.my.salesforce.com/services/data/v60.0/sobjects/Lead/` with a JSON body built from event and member placeholders.
  </Step>
</Steps>

### Bloomreach Engagement (Basic Auth)

<Steps>
  <Step>
    ### Create a Private API group

    In Bloomreach, open **Project Settings → Access Management → API → API Groups** and create a **Private** API group with the permissions your flow needs. Copy the API Key ID and API Secret.
  </Step>

  <Step>
    ### Create a Basic Auth Integration

    Create a **Basic Auth** Integration: Username = API Key ID, Password = API Secret.
  </Step>

  <Step>
    ### Configure the endpoint

    Point the block at the desired endpoint, e.g. `POST https://api.exponea.com/track/v2/projects/{projectToken}/customers/events`.
  </Step>
</Steps>

Public-token endpoints (`/track/v2/...`, consent fetch) alternatively work with a **Custom Headers** Integration carrying `Authorization: Token <api_token>` (marked secret). Private endpoints (`/data/v2/...`, `/api/v2/...`) require Basic Auth — default to Basic Auth if your flow touches anything beyond tracking.

### API-key platforms (Custom Headers)

For Iterable, Adobe, or your own webhook receiver: create a **Custom Headers** Integration with the vendor's key header (`Api-Key`, `X-Api-Key`, etc.), mark the value as secret, and reference it from the block.
