Skip to main content

Credentials

Stateway provides an encrypted credential store so you can reference secrets — API tokens, access keys, passwords — in process definitions without embedding them in plaintext.

Why not put secrets directly in BPMN​

Embedding secrets directly in task headers is dangerous:

<!-- ❌ Never do this -->
<stateway:taskHeader key="headers" value='{"Authorization": "Bearer sk_live_abc123..."}' />

The value is stored in plaintext in the source column of process_definitions, appears in XML exports (GET /definitions/:key/xml), and in any database dump. A definition leak becomes a credential leak.

Creating a credential​

Request:

curl -X POST https://api.stateway.io/v1/credentials \
-H "X-API-Key: sw_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "payment_api_token",
"value": "sk_live_abc123...",
"description": "Payment gateway API token"
}'

Response:

{
"data": {
"id": "cred_01j...",
"name": "payment_api_token",
"description": "Payment gateway API token",
"created_at": "2026-05-01T12:00:00.000Z"
}
}

The value is encrypted with AES-256-GCM before being persisted. The value is never returned by the API after creation. To replace it, use PATCH.

Rules for name:

  • Lowercase letters, digits, and underscores only
  • Must start with a lowercase letter
  • Maximum 64 characters
  • Examples: payment_api_token, fraud_service_key, internal_auth

Listing credentials​

Returns metadata only — values are never included.

Request:

curl https://api.stateway.io/v1/credentials \
-H "X-API-Key: sw_live_..."

Response:

{
"data": [
{
"id": "cred_01j...",
"name": "payment_api_token",
"description": "Payment gateway API token",
"created_at": "2026-05-01T12:00:00.000Z",
"updated_at": "2026-05-01T12:00:00.000Z"
}
]
}

Requires the credentials:read or admin scope.

Getting a credential by name​

Request:

curl https://api.stateway.io/v1/credentials/payment_api_token \
-H "X-API-Key: sw_live_..."

Response:

{
"data": {
"id": "cred_01j...",
"name": "payment_api_token",
"description": "Payment gateway API token",
"created_at": "2026-05-01T12:00:00.000Z",
"updated_at": "2026-05-01T12:00:00.000Z"
}
}

Requires the credentials:read or admin scope.

Using credentials in service tasks​

Reference a credential with the {{credentials.name}} syntax in the headers or authorization fields of taskHeaders, for serviceTask and sendTask only. It is never resolved in taskHeaders.url, and never in businessRuleTask — that type's taskHeaders is read as literal strings by design. See Supported Task Headers for the full header list:

<bpmn:serviceTask id="callPaymentApi" name="Charge Payment">
<bpmn:extensionElements>
<stateway:taskDefinition type="http" />
<stateway:taskHeaders>
<stateway:taskHeader key="url" value="https://api.payment.com/charge" />
<stateway:taskHeader key="method" value="POST" />
<stateway:taskHeader key="headers" value='{"Authorization": "Bearer {{credentials.payment_api_token}}"}' />
</stateway:taskHeaders>
</bpmn:extensionElements>
</bpmn:serviceTask>

The engine resolves the reference at execution time: the decrypted value exists only in memory during token execution and is never written to logs, the audit log, or webhook payloads.

See Variable Expressions for how {{credentials.name}} relates to the other expression positions and micro-syntaxes.

Rotating a credential​

To replace the value without touching any process definition that references it:

Request:

curl -X PATCH https://api.stateway.io/v1/credentials/payment_api_token \
-H "X-API-Key: sw_live_..." \
-H "Content-Type: application/json" \
-d '{ "value": "sk_live_xyz456..." }'

Response:

{
"data": {
"id": "cred_01j...",
"name": "payment_api_token",
"description": "Payment gateway API token",
"created_at": "2026-05-01T12:00:00.000Z",
"updated_at": "2026-05-13T09:00:00.000Z"
}
}

All instances started after the rotation will use the new value automatically. Instances already in progress that have not yet reached the service task will also pick up the new value.

You can also update the description without changing the value:

Request:

curl -X PATCH https://api.stateway.io/v1/credentials/payment_api_token \
-H "X-API-Key: sw_live_..." \
-H "Content-Type: application/json" \
-d '{ "description": "Payment gateway token (renewed May 2026)" }'

Response:

{
"data": {
"id": "cred_01j...",
"name": "payment_api_token",
"description": "Payment gateway token (renewed May 2026)",
"created_at": "2026-05-01T12:00:00.000Z",
"updated_at": "2026-05-13T09:05:00.000Z"
}
}

Deleting a credential​

Request:

curl -X DELETE https://api.stateway.io/v1/credentials/payment_api_token \
-H "X-API-Key: sw_live_..."

Returns 204 No Content. The deletion is soft — the credential is marked as deleted and no longer resolvable by the engine. Any process that references {{credentials.payment_api_token}} after deletion will fail at the service task with a credential resolution error.

Required scopes​

OperationRequired scope
GET /v1/credentialscredentials:read or admin
GET /v1/credentials/:namecredentials:read or admin
POST /v1/credentialscredentials:write or admin
PATCH /v1/credentials/:namecredentials:write or admin
DELETE /v1/credentials/:namecredentials:write or admin

Security limitations​

The current security model does not protect against:

  • Direct access to the application process (anyone who can exec into the container can decrypt)
  • Compromise of the Kubernetes cluster (K8s secrets would be accessible)
Values may be unrecoverable if the encryption key is lost

Stateway encrypts credentials with a platform-managed key. If that key is lost, rotated unexpectedly, or compromised, stored values may be permanently and irrecoverably lost.

Always keep independent, secure copies of your original credentials. The Credential Endpoint is a runtime reference mechanism — not a primary secret store.