# iPaaS and middleware

> Call the BatteryPass API from n8n, Make, Power Automate, MuleSoft, Boomi, Workato, Zapier and SAP Integration Suite with a key from the platform's secret store, and poll the change feed.

Most ERP integrations run on an integration platform rather than on custom code. Every platform below can call
BatteryPass, because the API needs only two things: an HTTPS request with the header
`Authorization: Bearer <key>`, and JSON (or `multipart/form-data` for imports). This page shows where the key goes
on each platform and how each one follows the [change feed](https://passport.kabin.sh/docs/en/api-reference/changes/list-changes).

The platforms are described from their public documentation. BatteryPass offers no connector of its own on any of
them and has no partnership with their vendors.

## What all platforms do the same way

- **The key lives in the platform's credential store**, never in a flow definition, a script or a variable that
  appears in run logs. Use one key per flow and environment (see [Security](https://passport.kabin.sh/docs/en/integrations/security)).
- **The contract.** BatteryPass publishes its integration contract as OpenAPI 3.0.3 at
  `/api/v1/openapi.json` and `/api/v1/openapi.yaml` on your BatteryPass address, and as a download in the
  [API reference](https://passport.kabin.sh/docs/en/api-reference). Platforms that import OpenAPI 3 build their operations from it.
- **Polling instead of webhooks.** BatteryPass sends no outbound webhooks yet; they are planned and need a
  decision of their own. Schedule a flow (every few minutes) that calls `GET /api/v1/changes` with the stored
  cursor, processes the items oldest first, and stores `next_cursor` when it is not `null`. The pattern is the same
  everywhere:

```text title="Polling loop"
cursor = load from the platform's data store (empty on the first run)
repeat:
  GET /api/v1/changes?types=passport.published,passport.republished&limit=100&cursor={cursor}
  for each item: read the entity, update the ERP
  if next_cursor is null: stop          (an empty page keeps the old cursor)
  cursor = next_cursor, save it          (only after the ERP accepted the page)
```

- **Errors.** Retry `429` after `Retry-After` and `5xx` with backoff; do not retry other `4xx` (see
  [Handle errors and retries](https://passport.kabin.sh/docs/en/guides/handle-errors)).

## Platforms

**n8n**

Use the **HTTP Request** node with a **Header Auth** credential: name `Authorization`, value
`Bearer bp_live_…` ([n8n: HTTP Request credentials](https://docs.n8n.io/integrations/builtin/credentials/httprequest/)).
The credential is stored encrypted and not shown in executions.

Polling: a **Schedule Trigger** starts the flow; keep the cursor in the workflow's static data or a small database
table, call `GET /changes` in an HTTP Request node, split the items, and loop while `next_cursor` is set. For
imports, send `multipart/form-data` with a binary property for `file` and a form field `preset`.

**Make**

Use the **HTTP** app and store the key in a keychain of type **API key** that adds the header `Authorization` with
`Bearer bp_live_…` ([Make: HTTP](https://apps.make.com/http)). Keychains keep the secret out of the scenario
blueprint.

Polling: a scheduled scenario, the cursor in a **Data store** record; the **Repeater** or an iterator walks the
pages until `next_cursor` is empty.

**Power Automate and Azure Logic Apps**

Custom connectors accept **only OpenAPI 2.0 (Swagger)** definitions, and files must stay under 1 MB; API key
authentication is supported ([Microsoft: custom connector FAQ](https://learn.microsoft.com/en-us/connectors/custom-connectors/faq)).
BatteryPass publishes OpenAPI 3.0.3 today, so you cannot import our contract as a custom connector directly. A
Swagger 2.0 export of the integration contract is planned as a follow-up.

Until then, use the built-in **HTTP** action: put the key into Azure Key Vault, read it with the Key Vault
connector (mark the output as secure so it does not appear in run history) and set the header
`Authorization: Bearer @{…}`. Polling: a **Recurrence** trigger, the cursor in a table or a variable stored in
Dataverse or Azure Table Storage, and a **Do until** loop over the pages.

**MuleSoft Anypoint**

**REST Connect** in Anypoint Exchange generates a connector from an OpenAPI 2.0 or 3.0 specification
([MuleSoft: REST Connect](https://docs.mulesoft.com/exchange/to-deploy-using-rest-connect)), so the published
`openapi.json` can be the source. Alternatively call the API with the HTTP Request connector.

Keep the key in secure configuration properties (encrypted) or a secrets manager, and set it as the `Authorization`
header of the connector configuration. Polling: a **Scheduler** source with the cursor in an Object Store.

**Boomi**

The **OpenAPI** connector imports OpenAPI 3.0 and later
([Boomi: OpenAPI connector](https://help.boomi.com/docs/Atomsphere/Integration/Connectors/int-OpenAPI_connector_6766251b-b601-41f5-8c13-86d79faf52b9)).
Import the published contract, or use the HTTP Client connector. Store the key as an encrypted connection field
or in an extension marked as password, and send it as the `Authorization` header.

Polling: a scheduled process with the cursor in a persisted process property.

**Workato**

Use the **HTTP** connector with a connection that sets the header `Authorization: Bearer …`; Workato encrypts
connection fields. Triggers that receive webhooks need a custom connector built with the connector SDK
([Workato: dynamic webhook triggers](https://docs.workato.com/en/developing-connectors/sdk/guides/building-triggers/dynamic-webhook.html));
since BatteryPass has no webhooks yet, use a **scheduled recipe** that polls `GET /changes` and keeps the cursor in a
lookup table.

**Zapier**

Zapier's instant triggers use **REST hooks**, which need subscribe and unsubscribe endpoints on the API side
([Zapier: REST hook trigger](https://docs.zapier.com/integrations/build/hook-trigger)). BatteryPass does not offer
them yet. Use a polling trigger instead: **Webhooks by Zapier** (Retrieve Poll) or a private app's polling trigger
against `GET /changes`, with the key in an API key authentication field. Zapier de-duplicates polled items by their
`id`, which every change carries.

**SAP Integration Suite (Cloud Integration)**

Use the **HTTP receiver adapter**. Store the key as a secure parameter in the tenant's security material and set the
header `Authorization` in a content modifier or script from it, never as a plain externalized parameter.

Watch out: Cloud Integration can drop custom headers from the message unless they are allowed. Add `Authorization`
(or the header you set) to **Allowed Header(s)** in the integration flow's runtime configuration; SAP Note 3075702
describes the symptom of headers not being sent. Polling: a **Timer** start event, the cursor in a data store or
global variable, and a looping process call over the pages.

## Uploading imports from a platform

[Units from production](https://passport.kabin.sh/docs/en/integrations/workflows/units-from-production) needs `multipart/form-data`: the
file in the part `file` (with a file name and a content type `text/csv` or the XLSX type), and the preset id in the
part `preset`. Every platform above can send multipart requests from its HTTP connector; build the CSV in the flow
and attach it as binary content. Then poll `GET /imports/{id}` until `status` is `validated` or `failed`.
