Integrations
Connections, grants, provider operations, webhooks.
Raw Markdown for agents: integrations.md. MCP: read_guide("integrations").
Integrations connect external systems (shops, CRMs, mail, accounting, databases) to the tenant. Three objects matter:
- A connection holds the credentials of one external account. It always belongs to the user who created it; only that user can call operations through it.
- A grant allows one connection inside one Space: which provider operations may run there, with optional constraints. Skills, workflows and agents working in a Space use grants.
- A provider operation is a call to the external system, registered as
integrations.<provider>.<resource>.<operation>(for exampleintegrations.shopify.orders.list). Every call is recorded as an invocation.
Flow
- Find the provider operations.
search_operations(provider="shopify")lists the operations of one provider; by default search only shows providers with a connection in the tenant, passconnected_only=falseto see all.integrations.catalog.getdescribes a provider:contract_digest, auth schemes, and per operation the input schema,risk_level,requires_spaceandconstraint_policy. - Check the connection.
get_contextlists the caller's connections with provider,connection_idand the number of active grants.integrations.connections.getshows status and setup state. Without a connection,integrations.authorizations.beginstarts one; OAuth schemes return anauthorization_urlthe user must open in the browser (offer it through the app, do not paste it into a message). Some providers need a setup step afterwards (integrations.connections.setup_options, thenintegrations.connections.complete_setup). - Create a grant with
integrations.grants.create:connection_id,space_idandallowed_operationswith full operation keys.integrations.grants.listshows existing grants; change one withintegrations.grants.updateinstead of creating a second grant for the same connection and Space. - Call the operation with
invoke. Arguments are the provider fields fromdescribe_operationplusconnection_id; operations inside a Space also takespace_idandgrant_id. Operations withrequires_spacealways need them; others can be called without a Space only when the provider allows direct owner use. Queued operations return aninvocation_id; pollintegrations.invocations.get. - Webhooks. Provider webhooks are not created by hand. When
skills.automation.setstores a webhook withauth: integrationandmode: subscription, the platform creates the provider subscriptions for the listedeventsthrough the grant ingrant_id. This happens asynchronously:skills.automation.getshowssubscription_statependinguntil the provider confirmed andactiveafterwards. A subscription that already exists at the provider for the same event and address is adopted, not duplicated. The grant must allow the provider's webhook subscription operations.
Rules
- Operation keys are always full keys:
integrations.shopify.orders.list, neverorders.listorshopify.orders.list. - Every operation with
risk_levelirreversibleinallowed_operationsneedsrisk_acknowledgements[<operation key>]set to the provider'scontract_digest. Ask the user to confirm the risk before you set it. - A grant belongs to exactly one connection and one Space. The connection must belong to the caller and the caller must have access to the Space.
safe_constraintshas the form{"version": 1, "constraints": {<operation key>: {<field>: {<rule>: <value>}}}}. Only fields and rules listed in the operation'sconstraint_policyare accepted (allowed_values, max_length, maximum, maximum_items, minimum, required_value).asset_boundariesnarrows a grant to part of the connection's setup selection; it cannot add values the setup did not select.- Errors raised by the platform itself carry a message,
detailswith the offending path and ahint: grant validation, input validation, missing permission for an operation, missing risk acknowledgement. Errors that come from the provider or the runner (timeouts, rate limits, provider rejections) only carry their code; the provider's own text is never passed through.
Errors and what to do
| Code | What to do |
|---|---|
integration_grant_policy_invalid | details names the entry, for example allowed_operations[2]; the hint shows the expected key format and the closest valid key. Fix that entry and retry. |
integration_invalid_input | details lists each invalid argument path; unknown parameters list the allowed ones. Compare with describe_operation. |
integration_operation_not_allowed | The connection exists but may not run this operation here: the grant does not allow it, space_id/grant_id are missing, or the setup does not offer it. Extend the grant with integrations.grants.update or ask the Space owner. |
integration_risk_acknowledgement_required | details has operation and contract_digest; set risk_acknowledgements[operation] = contract_digest after the user confirmed. |
integration_setup_required | Finish the connection setup with integrations.connections.setup_options and integrations.connections.complete_setup. |
integration_not_found | The connection, grant or Space does not exist for the caller, or belongs to someone else. Check the ids in get_context and integrations.grants.list. |
Example
Grant a Space read access to Shopify orders, at most 50 per page:
{
"operation_key": "integrations.grants.create",
"arguments": {
"connection_id": "<connection uuid>",
"space_id": "<space uuid>",
"allowed_operations": [
"integrations.shopify.orders.list",
"integrations.shopify.orders.get"
],
"safe_constraints": {
"version": 1,
"constraints": {
"integrations.shopify.orders.list": {"limit": {"maximum": 50}}
}
},
"idempotency_key": "grant-shopify-orders-<space uuid>"
}
}Then list paid orders through that grant:
{
"operation_key": "integrations.shopify.orders.list",
"arguments": {
"connection_id": "<connection uuid>",
"space_id": "<space uuid>",
"grant_id": "<grant id from the response>",
"query": "financial_status:paid created_at:>2026-09-01",
"limit": 50
}
}