Configuration
This guide covers how to configure integrations, use cases, and manage your Integration Toolkit setup.
Integration Managementโ
Integration Typesโ
Integrations support two types:
| Type | Description |
|---|---|
erp (default) | Standard ERP integration with inbound/outbound use cases |
connector | Complex proxy integration with external APIs using managed calls |
Creating an Integrationโ
curl -X POST 'https://integration-toolkit.sls.epilot.io/v2/integrations' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "SAP Integration",
"description": "Main ERP integration for customer and contract data"
}'
Creating a Connector Integration:
curl -X POST 'https://integration-toolkit.sls.epilot.io/v2/integrations' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Partner API Connector",
"description": "Connector for partner API calls",
"integration_type": "connector",
"connector_config": {
"base_url": "https://api.partner.com",
"auth": {
"type": "oauth2_client_credentials",
"token_url": "{{env.PARTNER_TOKEN_URL}}",
"client_id": "{{env.PARTNER_CLIENT_ID}}",
"client_secret": "{{env.PARTNER_CLIENT_SECRET}}",
"scope": "api:read api:write"
}
}
}'
Integration Propertiesโ
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
description | string | No | Human-readable description |
integration_type | string | No | erp (default) or connector |
connector_config | object | No | Shared config for connector-type integrations (base URL, auth) |
protected | boolean | No | When true, prevents deletion and restricts modifications to admin users |
maps | array | No | Key/Value Maps declared on this integration (key, label, description, value). Saving the integration writes each map to the organization's environment as a JSON variable. |
Listing Integrationsโ
curl -X GET 'https://integration-toolkit.sls.epilot.io/v2/integrations' \
-H 'Authorization: Bearer <token>'
Updating an Integrationโ
curl -X PUT 'https://integration-toolkit.sls.epilot.io/v2/integrations/{integrationId}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "SAP Production Integration",
"description": "Updated description"
}'
Deleting an Integrationโ
warning
Deleting an integration also removes all associated use cases.
curl -X DELETE 'https://integration-toolkit.sls.epilot.io/v2/integrations/{integrationId}' \
-H 'Authorization: Bearer <token>'
Use Case Configurationโ
Creating a Use Caseโ
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Customer Sync",
"type": "inbound",
"enabled": true,
"configuration": {
"entities": [...]
}
}'
Use Case Propertiesโ
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name for the use case |
type | string | Yes | inbound, outbound, file_proxy, managed_call, or secure_proxy |
enabled | boolean | Yes | Whether the use case is active |
configuration | object | Yes | Type-specific configuration (see sections below) |
info
inboundโ Mapped by default; set"direct": truein the configuration to skip mapping and send entity-shaped payloads directly. See Direct Mode.file_proxyโ On-demand file serving from external systems or outbound delivery of epilot files. See the File Proxy and Outbound File Delivery guides.managed_callโ Synchronous external API calls with JSONata mapping. See Managed Call Use Cases.secure_proxyโ Route requests through epilot's secure proxy for static IP or VPN access. See Secure Proxy Use Cases.
Inbound Use Case Optionsโ
Beyond the mapping itself, an inbound use case's configuration accepts the following options.
| Option | Values | Default | Description |
|---|---|---|---|
direct | boolean | false | Skip the mapping engine and accept entity-shaped payloads. See Direct Mode. |
interactive | disabled, on_request, always | disabled | Whether events of this use case may be processed on the interactive lane, so that an end customer can wait for them. See Interactive Registration. |
{
"entities": [ "..." ],
"interactive": "on_request"
}
on_requestis the normal setting: one mapping serves both the scheduled sync and the registration push, and each inbound request selects the interactive lane by sendinginteractive: true.alwaysmakes every event of the use case interactive, also on requests that do not ask, and charges every one to the organization's interactive budget. Reserve it for a use case that serves registrations only.disabledprocesses a request that asks for interactive as bulk and says so in each result; nothing is rejected. Absent meansdisabled, so remove the option rather than setting it todisabled.
In the Integration Hub, the option is the Interactive processing mode on the use case's General card.
If a caller is going to wait on your use cases by slug (wait_for_use_cases), model one inbound use case per partner event type and give every use case a slug. See Use Case Requirements.
Enabling/Disabling a Use Caseโ
curl -X PUT 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases/{useCaseId}' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"enabled": false
}'
Configuration propagation
Use case configurations are cached by the event-processing pipeline: any configuration change โ mapping edits, enabling/disabling, mode switches โ can take up to 5 minutes to take effect for incoming events. Plan cutovers accordingly.
Use Case Historyโ
View the change history for a use case:
curl -X GET 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases/{useCaseId}/history' \
-H 'Authorization: Bearer <token>'
Outbound Use Case Configurationโ
Outbound use cases deliver standardized epilot events (event-catalog events) to your external system. The configuration consists of an event_catalog_event and one or more mappings:
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Contract Sync",
"type": "outbound",
"enabled": true,
"configuration": {
"event_catalog_event": "contract.updated",
"mappings": [
{
"id": "b8f1c9a0-58dd-4f7a-9a3e-000000000001",
"name": "ERP Contract Sync",
"enabled": true,
"jsonata_expression": "{ \"id\": entity._id, \"customer\": entity.customer_name }",
"delivery": {
"type": "webhook",
"webhook_id": "wh-123"
}
}
]
}
}'
Event Filterโ
event_filter is an optional JSONata predicate on the use case configuration, next to event_catalog_event. It narrows which events of that name the use case handles โ for example, only tickets with a certain purpose, or only certain contract types:
{
"event_catalog_event": "CustomerRequestSubmitted",
"event_filter": "$count(ticket._purpose[$ = $env.move_request_purpose]) > 0",
"mappings": [ โฆ ]
}
- Input: the full hydrated event-catalog event, so relation nodes such as
ticketandcontactare populated. - Bindings:
$env(the organization's non-secret environment variables, including Key/Value Maps),$mapValueand$mapKey. Referencing$envinstead of hard-coding organization-specific values such as entity IDs keeps the filter portable between organizations, for example in a blueprint. - Result: the use case handles the event only when the filter evaluates truthy. When
event_filteris absent, every event of the configured name is handled. - Errors: a filter that throws while evaluating is treated as no match and logged, so one malformed filter cannot stop the other use cases subscribed to the same event.
- Validation on save: the filter must be a non-empty string, valid JSONata, and use no bindings other than
$env,$mapValueand$mapKey(names the expression binds itself with:=are allowed).
An event the filter rejects is not processed by the use case at all: no Pollable Outbound queue item and no file delivery. event_filter currently applies to poll and file proxy deliveries; it is not evaluated for webhook deliveries.
Mapping Propertiesโ
| Property | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | No | Unique identifier for the mapping; generated when omitted |
name | string | Yes | Display name for the mapping |
enabled | boolean | Yes | Whether this mapping is active |
jsonata_expression | string | For webhook delivery | JSONata expression to transform the event payload. Required for webhook (evaluated by the webhook service). Optional for poll: evaluated at enqueue time against the standardized event-catalog event with $env / $mapValue / $mapKey, must return a JSON object, and an empty value delivers the raw event โ see Payload Mapping. Rejected for file_proxy delivery |
delivery | object | Yes | How the event is delivered โ discriminated on type: webhook, poll, or file_proxy |
Outbound configurations are validated on save. The v1 use case endpoints require a jsonata_expression on every webhook mapping. The v2 integration upsert (POST / PUT /v2/integrations) validates an outbound use case only when it is new or its configuration changed, so configurations stored before a rule existed can be re-sent unchanged. It also accepts a webhook mapping with no jsonata_expression at all, for configurations that predate that requirement โ such a mapping never enables or updates its webhook. An empty expression and invalid JSONata are rejected on both versions.
Delivery Typesโ
Webhook delivery (push): the event payload is transformed with the mapping's jsonata_expression and pushed to a pre-configured webhook (epilot Webhooks):
"delivery": {
"type": "webhook",
"webhook_id": "wh-123"
}
| Property | Type | Required | Description |
|---|---|---|---|
webhook_id | string | Yes | Reference to the webhook configuration in epilot Webhooks |
webhook_name | string | No | Cached webhook name for display purposes |
Poll delivery (pull): for ERPs that cannot expose an inbound HTTP endpoint (firewalled, on-prem, batch systems). Items are placed on a pull-based queue that your system fetches and acknowledges. Poll items carry the raw standardized event payload, unless the mapping sets a jsonata_expression โ then they carry its output, evaluated once at enqueue time (see Payload Mapping). See Pollable Outbound for the full feature documentation (polling API, payload mapping, ordering guarantees, dead-letter handling, monitoring):
// DeliveryConfig โ poll variant
"delivery": {
"type": "poll", // discriminator: 'webhook' | 'poll'
"retention_days": 30, // optional; default 30, min 1, max 90
"poison_policy": "dead_letter", // 'dead_letter' (default) | 'block'
"max_delivery_attempts": 5 // delivery attempts before poison_policy applies
}
| Property | Type | Required | Default | Bounds | Description |
|---|---|---|---|---|---|
retention_days | integer | No | 30 | min 1, max 90 | How long undelivered queue items are retained before expiry |
poison_policy | string | No | "dead_letter" | dead_letter | block | What happens when an item exhausts max_delivery_attempts: dead_letter routes the item to the dead-letter queue; block halts the queue until action is taken |
max_delivery_attempts | integer | No | 5 | min 1, max 100 | Maximum delivery attempts before the poison_policy is applied |
info
- At most one poll mapping is allowed per use case. Webhook mappings may coexist alongside the poll mapping โ push and poll for the same event is allowed.
- A
polldelivery must not carry webhook fields (webhook_id,webhook_name), and awebhookdelivery must not carry poll fields (retention_days,poison_policy,max_delivery_attempts).
Everything beyond the configuration contract โ the polling and acknowledgement API, payload mapping and its preview endpoint, lease and ordering semantics, retention and expiry behavior, the dead-letter queue and operator actions, and poll-mode monitoring โ is documented on the dedicated Pollable Outbound page.
File proxy delivery (push): points to an upload-direction file_proxy use case in the same integration. The referenced recipe owns fan-out, payload mapping, authentication, and HTTP steps. jsonata_expression is rejected on this mapping type, and the use case's event_catalog_event must declare event_attachments. See Outbound File Delivery for the complete setup and runtime behavior.
"delivery": {
"type": "file_proxy",
"use_case_slug": "customer-request-document-upload"
}
| Property | Type | Required | Description |
|---|---|---|---|
use_case_slug | string | Yes | Slug of an upload-direction file_proxy use case in the same integration |
Mapping Configuration Schemaโ
Version 2.0 Structureโ
{
"entities": [
{
"entity_schema": "string",
"unique_ids": ["string"],
"enabled": "boolean | string",
"mode": "upsert | delete | purge | upsert-prune-scope-purge | upsert-prune-scope-delete",
"scope": {},
"jsonataExpression": "string",
"fields": [
{
"attribute": "string",
"field": "string",
"jsonataExpression": "string",
"constant": "any",
"_type": "email | phone",
"enabled": "boolean | string",
"relations": {
"operation": "_set | _append | _append_all",
"items": [
{
"entity_schema": "string",
"_tags": ["string"],
"unique_ids": [
{
"attribute": "string",
"field": "string"
}
]
}
],
"jsonataExpression": "string"
},
"relation_refs": {
"operation": "_set | _append | _append_all",
"items": [
{
"entity_schema": "string",
"unique_ids": [{ "attribute": "string", "field": "string" }],
"path": "string",
"value": {
"attribute": "string",
"operation": "_set | _append",
"field": "string",
"jsonataExpression": "string",
"constant": "any"
}
}
]
}
}
],
"pricing": {
"jsonataExpression": "string",
"items": [],
"items_jsonata": "string",
"result_mapping": {},
"on_error": "fail | skip | warn"
}
}
],
"meter_readings": [
{
"jsonataExpression": "string",
"mode": "upsert | delete | upsert-prune-scope",
"reading_matching": "external_id | strict-date",
"scope": { "source": "string" },
"meter": {
"unique_ids": [
{
"attribute": "string",
"field": "string"
}
]
},
"meter_counter": {
"unique_ids": [
{
"attribute": "string",
"field": "string"
}
]
},
"fields": []
}
]
}
Managed Call Use Casesโ
Managed call use cases define synchronous API operations against external partner systems. They are typically used with connector-type integrations.
Creating a Managed Call Use Caseโ
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Get Customer",
"slug": "get-customer",
"type": "managed_call",
"enabled": true,
"configuration": {
"operation": {
"method": "GET",
"path": "/v1/customers/{{customer_id}}",
"headers": {
"Accept": "application/json"
}
},
"response_mapping": "{ \"name\": data.full_name, \"email\": data.contact.email }",
"inbound_use_case_slug": "sync-customer"
}
}'
Executing a Managed Callโ
Managed calls are executed via the /v1/managed-call/{slug}/execute endpoint, where the slug acts as the RPC method name:
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/managed-call/get-customer/execute' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"integration_id": "<integration-id>",
"payload": {
"customer_id": "C001"
}
}'
Response:
{
"success": true,
"data": {
"name": "John Doe",
"email": "john@example.com"
},
"inbound_queued": true,
"inbound_event_id": "evt-abc-123"
}
Managed Call Configuration Fieldsโ
| Field | Required | Description |
|---|---|---|
operation.method | Yes | HTTP method: GET, POST, PUT, PATCH, or DELETE |
operation.path | Yes | URL path template with {{variable}} interpolation, appended to the connector's base_url |
operation.headers | No | Additional request headers |
operation.query_params | No | Query parameters |
request_mapping | No | JSONata expression to transform the request body before sending |
response_mapping | No | JSONata expression to transform the response before returning |
inbound_use_case_slug | No | Slug of an inbound use case to route the response to for async entity processing |
Connector Authenticationโ
Authentication is configured at the integration level via connector_config.auth and applied automatically to all managed call requests:
| Auth Type | Fields |
|---|---|
oauth2_client_credentials | token_url, client_id, client_secret, scope, audience, resource, body_params, headers, query_params |
api_key | api_key_header (default: X-API-Key), api_key |
bearer | token |
Secrets must use {{env.KEY}} references to resolve values from the Environments API.
Secure Proxy Use Casesโ
Secure proxy use cases route HTTP requests through epilot's dedicated proxy infrastructure, providing either a static IP for egress or VPN access to customer private networks.
Creating a Secure Proxy Use Caseโ
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Partner API Proxy",
"slug": "partner-api",
"type": "secure_proxy",
"enabled": true,
"configuration": {
"vpc_mode": "secure_link"
}
}'
Secure Proxy Configuration Fieldsโ
| Field | Required | Mutable | Description |
|---|---|---|---|
vpc_mode | Yes | No (immutable) | "static_ip" (fixed outbound IP) or "secure_link" (VPN for private networks) |
allowed_domains | No | Read-only | Array of allowed domain patterns. Supports exact match and wildcard prefix (e.g., *.example.com). Managed by epilot support. |
allowed_ips | No | Read-only | Array of allowed IP ranges in CIDR notation (e.g., 10.0.1.0/24). Required for secure_link mode. Managed by epilot support. |
Sending a Proxy Requestโ
curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/secure-proxy' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"integration_id": "<integration-id>",
"use_case_slug": "partner-api",
"url": "https://api.partner.com/v1/data",
"method": "GET",
"headers": {
"Authorization": "Bearer external-token"
}
}'
Domain Whitelist and IP Allowlistโ
- Domain whitelist: Controls which hostnames the proxy can reach. Wildcard patterns must have at least 2 suffix labels (e.g.,
*.example.comis valid,*.comis rejected). - IP allowlist: Controls which IP addresses are permitted in
secure_linkmode using CIDR notation. Validation is applied both at the URL level (direct IP targets) and DNS level (resolved IPs must match). - Both fields are read-only in the API. To add, remove, or change allowed domains or IP ranges, contact epilot support.
Securityโ
| Concern | Static IP mode | Secure Link mode |
|---|---|---|
| SSRF protection | Full (private IPs blocked) | Protocol + localhost only (private IPs allowed for VPN) |
| Domain whitelist | Optional | Required |
| IP allowlist | N/A | Required |
| Request size limit | 4 MB | 4 MB |
| Timeout | 30s | 30s |
Event Configurationโ
Event Message Structureโ
The structure of a POST /v3/erp/updates/events request:
{
"integration_id": "uuid",
"correlation_id": "string (optional)",
"interactive": "boolean (optional)",
"correlation_complete": "boolean (optional)",
"events": [
{
"use_case_slug": "string (recommended, 1-255 chars, pattern: ^[a-z0-9][a-z0-9_-]*$, required if event_name is not provided)",
"event_name": "string (legacy fallback, required if use_case_slug is not provided)",
"timestamp": "ISO 8601 datetime",
"format": "json | xml",
"payload": "string (JSON or XML)",
"deduplication_id": "string (optional)"
}
]
}
For each event, include use_case_slug whenever possible. event_name is supported as a legacy fallback, and use_case_slug takes precedence if both are present.
v3 takes correlation_id at request level and has no event_type. The deprecated v1 and v2 endpoints take
event_type and, on v1, a meta object; v3 drops both without an error, so a meta.correlation_id sent to v3 is
lost. interactive and correlation_complete are for pushes an end customer is waiting for โ see
Interactive Registration.
use_case_slug format: ^[a-z0-9][a-z0-9_-]*$ with length 1..255 (must start with a lowercase letter or digit; then lowercase letters, digits, _, -).
Event Types (v1 and v2)โ
The deprecated v1 and v2 endpoints classify each event with event_type. v3 has no such field.
| Type | Description |
|---|---|
CREATE | Create a new entity (fails if exists) |
UPDATE | Update existing or create new entity |
DELETE | Mark entity as deleted |
Payload Formatsโ
JSON Format:
{
"format": "json",
"payload": "{\"customerId\":\"C001\",\"name\":\"John Doe\"}"
}
XML Format:
{
"format": "xml",
"payload": "<customer><id>C001</id><name>John Doe</name></customer>"
}
Deduplicationโ
Deduplication IDโ
Prevent duplicate processing by including a unique deduplication ID:
{
"events": [
{
"deduplication_id": "customer-C001-update-20240115-001",
"payload": "..."
}
]
}
Deduplication Windowโ
- Events are deduplicated within a 24-hour window
- Same deduplication ID within 24 hours = skipped
- After 24 hours, the same ID can be processed again
Best Practicesโ
# Good - includes version or timestamp
customer-C001-update-v3
customer-C001-update-20240115T103000
# Bad - no version control, may skip legitimate updates
customer-C001
Correlation Trackingโ
Correlation IDโ
Include a correlation ID at request level to track related events:
{
"integration_id": "uuid",
"correlation_id": "<uuidv4()>",
"events": [...]
}
When you omit it, epilot generates one and returns it in the response. For a correlation id an end customer is waiting on, see Interactive Registration.
Using Correlation IDsโ
- Group related events for debugging
- Filter monitoring data by batch
- Trace event processing through the pipeline
Monitoringโ
Every inbound and outbound event the toolkit processes is recorded as a monitoring event, queryable per integration and visible in the Integration Hub's Monitoring tab. A one-off example โ the last day's error events for one integration:
curl -X POST 'https://integration-toolkit.sls.epilot.io/v2/integrations/{integrationId}/monitoring/events' \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{ "level": "error", "from_date": "2026-01-15T00:00:00Z", "limit": 50 }'
Querying, stats and time series, event traces, replay, the code reference, and alerting are documented in the Monitoring section.
Rate Limitsโ
| Endpoint | Limit |
|---|---|
| Event submission | 100 events per request |
| API requests | 1000 requests per minute per organization |
Securityโ
Authenticationโ
All API requests require a valid Bearer token:
curl -H 'Authorization: Bearer <your-token>' ...
Organization Isolationโ
Each organization's data is fully isolated. Integration IDs are scoped to organizations, and cross-organization access is not permitted.
Permissionsโ
Four actions govern the Integration Toolkit. They are checked against the organization the token belongs to, and โ where the endpoint names one โ against the specific integration.
| Permission | Grants |
|---|---|
integration:view | Read integrations and use cases; read monitoring events, stats, time series, traces, access logs, notification history and status |
integration:manage | Create, update and delete integrations and use cases; edit notification configuration; push external monitoring events; send test notifications; replay events |
integration:ingest | Push inbound events to the integration (POST /v1/erp/updates/events, /v2/โฆ, /v3/โฆ). It grants nothing else โ not reading what landed, and not replay |
integration:consume | Poll and acknowledge the outbound queue. Scoped to Pollable Outbound only โ it grants nothing else |
The view / manage split is the one that catches people out when minting a scoped
token for a middleware:
| Endpoint | Needs |
|---|---|
POST /v2/integrations/{id}/monitoring/events, โฆ/stats, โฆ/time-series | integration:view |
GET /v2/integrations/{id}/monitoring/traces/{correlationId} | integration:view |
GET /v2/integrations/{id}/notifications/history, โฆ/status | integration:view |
POST /v2/integrations/{id}/monitoring/external-events | integration:manage |
POST /v2/integrations/{id}/notifications/test | integration:manage |
POST /v1/integrations/{id}/events/replay | integration:manage, checked organization-wide |
POST /v3/erp/updates/events (and v1, v2) | integration:ingest |
GET /v3/erp/updates/events/{event_id}, GET /v3/erp/updates/events?correlation_id=โฆ, GET /v3/erp/updates/correlations | integration:view |
POST /v1/integrations/{id}/outbound/messages/poll, โฆ/ack | integration:consume |
The monitoring events list (POST /v2/integrations/{id}/monitoring/events) returns
403 without integration:view on that integration. Its event details can carry customer
data โ a mapped poll message
includes its delivered payload.
Inbound event submission needs integration:ingest on the integration it pushes to;
integration:* and the owner role include it. See Inbound Getting Started. A replay of inbound events
needs integration:manage instead; a push token's integration:ingest does not cover it.