Skip to content

REST APIs and GraphQL ​

This page covers two Explorer screens. REST APIs lists the HTTP APIs the tenant already has (a data API per entity, call addresses per flow, one door for connectors) and publishes key-protected addresses for outside systems. GraphQL manages GraphQL, SOAP and SFTP connections and calls them for testing. Readers are integration developers and administrators.

REST APIs ​

Where to find it ​

Studio Explorer > Workspace > Integrations > REST APIs. Page key api-catalog. Two tabs: What exists and Published for other systems.

What exists ​

The catalog is built from live records; there is nothing separate to maintain. Designing an entity, a flow or a connector is what publishes its API. A banner states that every call needs the X-Tenant-Id header (the tenant number is shown) and a signed-in session, and that the caller's permissions decide what is allowed.

KindOne row perMethodAddressNote
DataActive entityGET POST PUT DELETE/api/v1/entities/ENTITY/records"Search, read, create, change and delete records. Permissions of the caller apply."
FlowFlowPOST/api/v1/integration/flows/CODE/execute"Starts the flow for a signed-in caller; returns an execution id."
FlowFlow, "(from another system)"POST/api/v1/integration/flows/webhooks/CODE"Signed requests only." or "Open to anyone with the address. Protect it under Webhooks > Receiving."
ConnectorConnector definition in the integration domainPOST/api/v1/integration/runSend {"kind":"connector","key":"KEY","input":{...} }. "Switched off." is appended for inactive connectors.

Chips filter by kind (All, Data, Flows, Connectors) with counts. Each row has Copy address, which copies the address with the server base. If a source cannot be read the screen warns "Could not list data, flows, connectors. You may not have permission to see them."

Running a flow or connector from a page, a workflow or code ​

POST /api/v1/integration/run runs one connector now (the answer is returned) or queues one flow (an execution id is returned). The body is {"kind":"connector|flow","key":"...","input":{...} }; the header X-Idempotency-Key is honoured. Responses:

json
{ "kind": "flow", "key": "order-to-partner", "ok": true, "executionId": 412, "status": "QUEUED" }
json
{ "kind": "connector", "key": "slack-alerts", "ok": true, "status": 200, "reference": null, "mappedStatus": null, "url": null, "response": { "ok": true } }

Permission integration.run / execute. Limit 120 runs a minute per tenant ("too many integration runs in the last minute; try again shortly", 429). Errors: 400 "say what to run: kind (connector or flow) and key", 404 no flow named "X" or no connector named "X", 409 connector "X" is switched off, 422 when a flow cannot be queued, 502 with the provider's message when a connector call fails. Each run is audited.

Published for other systems ​

A published API lets an outside system run one connector or flow with a key, without a Studio login. It starts switched off.

Publish an API dialog ​

FieldTypeDefaultRequiredDescription
NametextemptyYesThe address name is derived: "Address ends with /published/SLUG".
RunsselectnoneYesA connector or a flow from a combined list. "A connector answers at once. A flow is queued and returns an execution id."
Calls allowed per minutenumber60NoPer API.

The slug is lower-case letters, digits and dashes, up to 63 characters. The grid shows API (name and what it runs), Address (POST BASE/api/v1/integration/published/SLUG), Keys (active count), Limit and Status.

Keys ​

Row action Keys lists each key with its name, short prefix (spk_...) and "last used TIME" or "never used", and Revoke. Make one key per calling system so one can be stopped without affecting the others. New key requires a name ("name the key after the system that will use it"); the key is shown once with a ready curl example:

bash
curl -X POST "https://erp.example.com/api/v1/integration/published/partner-orders" \
  -H "X-Tenant-Id: 1" -H "X-Api-Key: spk_xxxxxxxx" \
  -H "Content-Type: application/json" -d '{"orderNo":"A-1001"}'

The key may be sent as X-Api-Key or as Authorization: Bearer KEY. The body must be a JSON object up to 256 KB.

Behaviour ​

RuleValue
Unknown API, switched-off API and wrong keyThe same answer, 401 "missing or incorrect API key", so a caller learns nothing about what exists.
Rejected keysAfter 30 rejected calls a minute per tenant, 429 "too many rejected calls; try again shortly".
Per-API rate429 with Retry-After: 60 and "this API allows N calls a minute".
Body400 "the body must be a JSON object"; 413 "the request body is larger than 256 KB".
IdempotencyThe header X-Idempotency-Key.
AuditEach run is audited under the key's short prefix, never the key. The key itself is stored hashed.
Switch off, deleteSwitch off stops calls at once; deleting removes the API and its keys.

Procedure: let a partner start a flow ​

  1. On Published for other systems select Publish an API. Name Partner orders, Runs order-to-partner (flow), 60 calls a minute. Select Publish.
  2. Select Switch on.
  3. Select Keys, enter Partner ACME, select New key and copy the key.
  4. Give the partner the address and the key. They call it; the response is {"kind":"flow","key":"order-to-partner","ok":true,"executionId":N,"status":"QUEUED"}.
  5. Open the flow's history on Integration Flows to see the run, trigger type API.

Permissions ​

integration.api: view (list), manage (publish, switch, delete, create and revoke keys). Calling a published API needs only the key. Without permission the screen shows the 403 message.

API ​

PurposeRequest
ListGET /api/v1/integration/apis (each with slug, name, kind, targetKey, active, rateLimitPerMin, path, keys)
PublishPOST /api/v1/integration/apis with {"slug":"partner-orders","name":"Partner orders","kind":"flow","targetKey":"order-to-partner","rateLimitPerMin":60}
SwitchPOST .../{slug}/active with {"active":true}
DeleteDELETE .../{slug}
Create keyPOST .../{slug}/keys with {"name":"Partner ACME"} (returns key once)
Revoke keyDELETE /api/v1/integration/apis/keys/{keyId}
CallPOST /api/v1/integration/published/{slug}

There is no erp command to manage published APIs.

Errors and troubleshooting ​

MessageCause
"the address name must be lower-case letters, digits and dashes"Bad slug.
"an API runs a connector or a flow"kind is neither.
"choose what the API runs" / "give the API a name"
"there is already an API called X" (409)Slug taken.
"no API X" (404)
"name the key after the system that will use it"Empty key name.
"no active key N" (404)Key already revoked.

GraphQL ​

The Explorer node opens the Connectors screen on its Integration tab. It manages connections of type GraphQL, SOAP and SFTP (REST connections are managed on Connections, where the same records also appear) and has a test console for each row. The other tabs of the same screen are described under External Connectors.

Where to find it ​

Studio Explorer > Workspace > Integrations > GraphQL. Page key graphql-connections (the Connectors screen, tab Integration).

New Integration Connection dialog ​

FieldTypeDefaultRequiredDescription
Connection CodetextemptyYesDisabled when editing.
TypeselectGRAPHQLYesGRAPHQL, SOAP or SFTP. Disabled when editing.
NametextemptyYes
Base URLtextemptyYesThe endpoint. For SFTP sftp://host:22.
Auth TypeselectNONEYes (not SFTP)NONE, API_KEY, BASIC, BEARER, OAUTH2_CLIENT_CREDENTIALS.
SecretpasswordemptyWhen auth needs one"Leave blank to keep the current one" when editing.
UsernametextemptySFTPSaved in the connection's settings. SFTP always signs in with a password here.

For richer set-up (key sign-in, host key pinning, OAuth fields) use the Connections screen.

Test console (expand a row) ​

TypeInputsResult
GRAPHQLQuery (default { __typename }), Variables (JSON, default {}), ExecuteJSON data on success; Failed: ERRORS when the errors array is non-empty or the transport failed. A GraphQL answer with errors is a failure even on HTTP 200.
SOAPSOAPAction, Body XML (default <Body/>), CallThe response body XML, or Fault CODE: MESSAGE for a SOAP 1.1 fault, including faults on a non-2xx status.
SFTPRemote Directory (default /), List Files, Health CheckA file table (name, size, directory) and a Healthy or Unreachable chip.

API ​

PurposeRequest
GraphQLPOST /api/v1/integrations/connections/{code}/graphql with {"query":"...","variables":{} }
SOAPPOST .../{code}/soap with {"soapAction":"...","bodyXml":"..."}
SFTP listGET .../{code}/sftp/files?remoteDirectory=/
SFTP downloadGET .../{code}/sftp/download?remotePath=... returns contentBase64
SFTP uploadPOST .../{code}/sftp/upload with {"remotePath":"...","contentBase64":"..."}
SFTP deleteDELETE .../{code}/sftp?remotePath=...
SFTP healthGET .../{code}/sftp/health

Note the path prefix /api/v1/integrations/ (plural) for these direct calls, against /api/v1/integration/ for everything else. Permission resource integration: execute for every call except SFTP health, which needs view. A connection of the wrong type is refused with 422 connection "X" is connection_type TYPE, not EXPECTED; an unknown code returns 404 no connection "X". Direct calls are audited (integration.graphql.executed, integration.soap.executed, integration.sftp.uploaded, integration.sftp.deleted).

Flows reach SFTP through the sftp.* actions on an SFTP connection, and databases through db.query and db.write. GraphQL and SOAP connections are not called by a flow step in this release; a flow that needs one makes a REST step to the direct-call endpoint above, or calls the service as an ordinary REST connection.

bash
curl -X POST https://erp.example.com/api/v1/integrations/connections/crm-graphql/graphql \
  -H "X-Tenant-Id: 1" -H "X-Actor: alice" -H "Content-Type: application/json" \
  -d '{"query":"query($id: ID!) { customer(id: $id) { name } }","variables":{"id":"42"}}'

The response is {"success":true,"data":{...},"errors":[],"rawErrorMessage":null}.

Connections, Flows, Connectors, API designer, Connect to another system.