Skip to main content

Configuration

This guide covers how to configure integrations, use cases, and manage your Integration Toolkit setup.

Integration Managementโ€‹

Integration Typesโ€‹

Integrations support two types:

TypeDescription
erp (default)Standard ERP integration with inbound/outbound use cases
connectorComplex 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โ€‹

PropertyTypeRequiredDescription
namestringYesDisplay name
descriptionstringNoHuman-readable description
integration_typestringNoerp (default) or connector
connector_configobjectNoShared config for connector-type integrations (base URL, auth)
protectedbooleanNoWhen true, prevents deletion and restricts modifications to admin users
mapsarrayNoKey/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โ€‹

PropertyTypeRequiredDescription
namestringYesDisplay name for the use case
typestringYesinbound, outbound, file_proxy, managed_call, or secure_proxy
enabledbooleanYesWhether the use case is active
configurationobjectYesType-specific configuration (see sections below)
info
  • inbound โ€” Mapped by default; set "direct": true in 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.

OptionValuesDefaultDescription
directbooleanfalseSkip the mapping engine and accept entity-shaped payloads. See Direct Mode.
interactivedisabled, on_request, alwaysdisabledWhether events of this use case may be processed on the interactive lane, so that an end customer can wait for them. See Interactive Registration.
Allow the request to decide
{
"entities": [ "..." ],
"interactive": "on_request"
}
  • on_request is the normal setting: one mapping serves both the scheduled sync and the registration push, and each inbound request selects the interactive lane by sending interactive: true.
  • always makes 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.
  • disabled processes a request that asks for interactive as bulk and says so in each result; nothing is rejected. Absent means disabled, so remove the option rather than setting it to disabled.

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 ticket and contact are populated.
  • Bindings: $env (the organization's non-secret environment variables, including Key/Value Maps), $mapValue and $mapKey. Referencing $env instead 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_filter is 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, $mapValue and $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โ€‹

PropertyTypeRequiredDescription
idstring (UUID)NoUnique identifier for the mapping; generated when omitted
namestringYesDisplay name for the mapping
enabledbooleanYesWhether this mapping is active
jsonata_expressionstringFor webhook deliveryJSONata 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
deliveryobjectYesHow 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"
}
PropertyTypeRequiredDescription
webhook_idstringYesReference to the webhook configuration in epilot Webhooks
webhook_namestringNoCached 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
}
PropertyTypeRequiredDefaultBoundsDescription
retention_daysintegerNo30min 1, max 90How long undelivered queue items are retained before expiry
poison_policystringNo"dead_letter"dead_letter | blockWhat 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_attemptsintegerNo5min 1, max 100Maximum 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 poll delivery must not carry webhook fields (webhook_id, webhook_name), and a webhook delivery 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"
}
PropertyTypeRequiredDescription
use_case_slugstringYesSlug 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โ€‹

FieldRequiredDescription
operation.methodYesHTTP method: GET, POST, PUT, PATCH, or DELETE
operation.pathYesURL path template with {{variable}} interpolation, appended to the connector's base_url
operation.headersNoAdditional request headers
operation.query_paramsNoQuery parameters
request_mappingNoJSONata expression to transform the request body before sending
response_mappingNoJSONata expression to transform the response before returning
inbound_use_case_slugNoSlug 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 TypeFields
oauth2_client_credentialstoken_url, client_id, client_secret, scope, audience, resource, body_params, headers, query_params
api_keyapi_key_header (default: X-API-Key), api_key
bearertoken

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โ€‹

FieldRequiredMutableDescription
vpc_modeYesNo (immutable)"static_ip" (fixed outbound IP) or "secure_link" (VPN for private networks)
allowed_domainsNoRead-onlyArray of allowed domain patterns. Supports exact match and wildcard prefix (e.g., *.example.com). Managed by epilot support.
allowed_ipsNoRead-onlyArray 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.com is valid, *.com is rejected).
  • IP allowlist: Controls which IP addresses are permitted in secure_link mode 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โ€‹

ConcernStatic IP modeSecure Link mode
SSRF protectionFull (private IPs blocked)Protocol + localhost only (private IPs allowed for VPN)
Domain whitelistOptionalRequired
IP allowlistN/ARequired
Request size limit4 MB4 MB
Timeout30s30s

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.

TypeDescription
CREATECreate a new entity (fails if exists)
UPDATEUpdate existing or create new entity
DELETEMark 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โ€‹

EndpointLimit
Event submission100 events per request
API requests1000 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.

PermissionGrants
integration:viewRead integrations and use cases; read monitoring events, stats, time series, traces, access logs, notification history and status
integration:manageCreate, update and delete integrations and use cases; edit notification configuration; push external monitoring events; send test notifications; replay events
integration:ingestPush inbound events to the integration (POST /v1/erp/updates/events, /v2/โ€ฆ, /v3/โ€ฆ). It grants nothing else โ€” not reading what landed, and not replay
integration:consumePoll 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:

EndpointNeeds
POST /v2/integrations/{id}/monitoring/events, โ€ฆ/stats, โ€ฆ/time-seriesintegration:view
GET /v2/integrations/{id}/monitoring/traces/{correlationId}integration:view
GET /v2/integrations/{id}/notifications/history, โ€ฆ/statusintegration:view
POST /v2/integrations/{id}/monitoring/external-eventsintegration:manage
POST /v2/integrations/{id}/notifications/testintegration:manage
POST /v1/integrations/{id}/events/replayintegration: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/correlationsintegration:view
POST /v1/integrations/{id}/outbound/messages/poll, โ€ฆ/ackintegration: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.