Create a connection (store credentials for one piece)
connection:writeStores a credential set for exactly ONE piece. A connection is not a generic secret store: it belongs to pieceId (e.g. slack, postgres, googlesheets) and a slack connection can never be used by a postgres step.
HOW TO KNOW WHAT GOES IN config: the credential fields are declared by the PIECE, not by this endpoint. Call GET /api/pieces/{pieceId} and read its auth object — auth.required says whether the piece uses a connection at all, auth.type names the shape (none, api_key, bearer_token, basic_auth, oauth2, custom), and auth.fields[] lists {name, title, type, required, description}. Every key you put in config is an auth.fields[].name; a field of type: "password" is a secret. For an OAuth2 piece, auth.oauth2 carries authorizationUrl, tokenUrl and default scopes, so you normally supply only clientId/clientSecret here and then run POST /api/connections/{id}/oauth2/start.
VALIDATION AGAINST THE FLEET: when the live worker fleet has published a catalog, pieceId must be a piece the fleet actually runs AND that piece must declare auth.required: true — otherwise 400 ("Unknown piece" / "This piece does not use a connection"). config itself is NOT validated field-by-field: missing or misspelled credential keys are accepted silently and only surface when the connection is tested or used in a run.
authType is a free-text label recorded on the row; it is NOT checked against the piece and does not change how the credential is used. Omitted, it defaults to "api_key".
STATUS ON CREATE: normally active. The one exception is the generic oauth2 piece with no accessToken in config — that row is created pending, because client credentials alone cannot call anything until the authorization flow has run.
USING IT FROM A WORKFLOW: reference it BY ID. A step or trigger in a workflow definition carries "connectionId": "<this id>" next to its pieceId; credentials are never inlined into a step's config. The connection must be in the same workspace as the workflow.
The response echoes the created row and, like every connection response, contains no credential material.
Authorization
bearerAuth An API token: Authorization: Bearer grt_…. Mint one in the app under Settings → Workspace → API tokens; the raw value is shown once. Scopes confine the token — see x-permission on each operation.
In: header
Header Parameters
Optional opt-in de-duplication key, 255 characters max. Retrying with the SAME key and the same method + path + query + body replays the first response verbatim without performing the write again, and the replay carries the response header Idempotency-Replayed: true. Records live 24 hours. The same key with a DIFFERENT request is refused 422; a duplicate arriving while the first is still running is answered 409 after a short wait. Only successful writes are recorded, so a failed call can be retried with the same key. Use a fresh UUID per logical operation.
length <= 255Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/api/connections" \ -H "Idempotency-Key: 7c1f5b6a-4f4b-4a8a-9c1e-9c2f3a5d7e11" \ -H "Content-Type: application/json" \ -d '{ "pieceId": "slack", "name": "Slack — alerts bot", "authType": "api_key", "config": { "botToken": "xoxb-..." } }'{ "data": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "pieceId": "string", "name": "string", "authType": "string", "status": "active", "lastTestedAt": "2019-08-24T14:15:22Z", "tokenExpiresAt": "2019-08-24T14:15:22Z", "workspaceId": "ef0efa32-d1c1-43d4-a5e2-fe7b4f00403c", "orgId": "25b2c2d5-a7fc-47d0-89e4-8709a1560bfa", "createdBy": "25a02396-1048-48f9-bf93-102d2fb7895e", "createdAt": "2019-08-24T14:15:22Z", "updatedAt": "2019-08-24T14:15:22Z" }}