The task field is spelled
connection_id and its value is a cred_ id. The field name is fixed by workflow-history compatibility and is not going to change; read it as “the credential this task runs with”. Built-in utility actions (like getdialed__utils__echo) need none — their service reports requires_connection: false.One credential, many services
The thing you actually possess is a login, not a login-per-service. So one credential record covers every service of its tenant that accepts its authentication method:- You create it once. Supply the platform, the authentication method and the secret material. There is no service to pick.
- It is offered wherever it fits. A Five9 basic-auth credential is available to every Five9 service that takes basic auth, on the Five9 account it authenticates to.
- You rotate it once. One update reaches every flow using it — see rotating a secret.
The credential object
Authentication methods
auth_method describes only the credential material and how it is presented. It says nothing about the transport — whether a service speaks SOAP or REST is the service’s own protocol field in the catalog.
A catalog service publishes the method it accepts as its own
auth_method, which additionally may be none — meaning the service needs no credential at all. A credential that presents nothing is not a credential, so none is not a value a credential can have.
Basic auth: two ways to supply it, one stored shape
Abasic_auth credential is always stored as a username and a password. Supply it either way:
basic_token is the convenience input for when what you have on hand is the pre-encoded token a provider’s documentation prints. It is decoded and stored as the username and password pair, and the encoded form is never kept. Supply one or the other, never both — they are two encodings of one identity, and a body carrying both is refused.
base64(username:password) is a wire encoding, not a credential format: encoding is the transport’s business, and the Authorization header is computed fresh each time the credential is used. Storing both forms would mean two records for one real login, which is exactly what “one credential, rotated once” is meant to prevent.
A basic_token that is not valid base64, or that decodes to something without a colon in it, is refused with a message naming the field only — never any part of the token.
Declaring where an API key goes
Anapi_key credential needs one more fact than the key itself: where the key is presented. Providers disagree — some want a header, some want Bearer in front of the value, some want a query parameter — and the key alone does not say which.
That fact is declared once, on the credential, as api_key_placement. Exactly one of header_name or query_param, and scheme only alongside header_name:
Postmark server token
Postmark takes the second shape. A transactional email credential is anapi_key credential whose key is presented in an X-Postmark-Server-Token header, with no scheme in front of it:
server_token to say so at the point where it is supplied.
One credential is one Postmark server. GetDialed calls Postmark with the token, reads back which server it belongs to, and files the credential under that Server — you never look up or type a provider identifier.
Why the placement lives on the credential
Declaring it here rather than per step is what makes the guarantee hold: every step using this credential authenticates identically, and no step configuration mentions authentication at all. A placement that could be overridden per use would put the same credential on the wire two different ways and reintroduce the “which one is this step actually doing” question. It also decides what a step may not author. A step that selects this credential cannot set the header or query parameter the placement names — that name joins the three that are always refused. See calling an HTTP API. A placement declaring neither a header nor a query parameter, declaring both, or puttingscheme on a query parameter is refused: each is either no answer to “where does the key go” or two answers.
OAuth2 and audience
An oauth2 credential exchanges its client id and secret at token_url for an access token, and the platform refreshes that token for you. Alongside grant_type and scopes, some authorization servers also require an audience — their own identifier for the API the token is being requested for.
audience is the first thing to check in the provider’s documentation.
token_url is required for oauth2 and rejected on every other method, and only an https token endpoint is ever contacted — a plain http one is refused when the exchange runs. grant_type, scopes and audience are likewise oauth2-only, and api_key_placement is api_key-only and required there: a credential carrying configuration that belongs to a different method is refused, because it is a second and contradictory answer to what the credential presents.
Both audience and api_key_placement are non-secret configuration. They are returned by the API like any other field, and a PUT can change either without touching the stored material — so correcting a placement is not a rotation and does not clear last_verified.
AWS access key
Anaws_access_key credential is the access-key pair of an IAM user in your AWS account, plus the region it works in. It is what the S3 file steps authenticate with, and it reaches nothing in your account that its IAM policy does not name.
credentials accepts exactly those two keys. A third — a session token, say — is refused rather than quietly dropped, because nothing ever reads one back out and storing an unread secret is worse than not accepting it.
aws_region is required on this method and refused on every other, as are the other two aws_ fields. A credential carrying configuration belonging to a different method is refused: it is a second and contradictory answer to what the credential presents.
aws_default_bucket is the bucket a storage step uses when it names none itself. Set it and the bucket name lives in one place instead of in every step. It is a bucket name, not a URL or a path — a value containing /, :// or whitespace is refused.
There is no api-key placement to declare, and that is why this is its own method rather than a variant of
api_key. An access-key pair is not presented as a header value: it signs the request, and the signature covers the whole request. There is nowhere to put the key, so api_key_placement would be simultaneously required and meaningless.The IAM policy to attach
Scope the IAM user to the one bucket. Store a report in S3 prints the exact two-statement policy, including whys3:ListBucket on the bucket ARN belongs on it even though nothing lists your bucket — without it, S3 answers 403 for an object that is simply not there, and a missing object becomes indistinguishable from a broken credential.
S3-compatible storage
aws_endpoint_url points the credential at an S3-compatible service instead of Amazon’s — Cloudflare R2, MinIO, Backblaze B2, DigitalOcean Spaces. One field; the storage steps are otherwise identical.
Set it together with aws_default_bucket. An S3-compatible provider exposes no account-identity call, so reaching the bucket is the only way to establish that the key pair works at all.
Leave the field empty for real AWS. A trailing newline or stray whitespace still counts as a set endpoint, so paste carefully — an R2 credential is not silently sent to Amazon.
Testing an AWS credential
POST /credentials/{credential_id}/test makes two checks, not one, and reports which of them it managed:
- It resolves the account identity, proving the pair is valid. This call requires no IAM permission and cannot be denied by a policy, which is exactly what makes it a good identity check and a worthless permission check.
- If
aws_default_bucketis set, it then checks it can reach that bucket — the grant you actually need.
POST /credentials/test — the test-before-save endpoint — cannot test an AWS credential yet. Its body carries no aws_region, and the region is not optional, so the attempt comes back as INVALID_CREDENTIALS_SHAPE. Create the credential and test it by id instead; a create that cannot be verified fails before anything is stored, so nothing is left behind.Not supported yet
Cross-account IAM roles. There is no way to give GetDialed a role to assume with an external id — the access-key pair is the only AWS credential shape today. If your security posture requires roles rather than long-lived keys, that is a real gap and it is worth waiting for rather than designing around.Which credential an action can use
Compatibility is computed by the server, and the same rule runs in both places it matters — when you list candidates and when you save a definition. A credential offered to you will not be rejected at save time.GET /credentials?compatible_with={action_id} returns only the credentials that action can actually use:
- For a vendor action, the credential’s tenant must belong to the action’s platform and its
auth_methodmust be one the action’s service accepts. An unbound credential is never offered for a vendor action — it has no tenant to have authenticated to. - For a built-in action, the authentication method alone decides; there is no vendor tenant to compare against.
- An action that needs no credential matches none. You get an empty list rather than every credential you own.
- An unknown
action_idis a404, not an empty list.
422.
Rotating a secret
Supplycredentials on PUT /credentials/{credential_id} to replace the stored material. The credential keeps its id and keeps pointing at the same stored secret, so:
- No flow edit. Every definition referencing this credential picks up the new material.
- No re-selection and no redeployment.
- A running worker refreshes its cached copy within 300 seconds — so allow up to five minutes before expecting the new material to be in force everywhere, and do not rotate and immediately re-dispatch expecting the new value.
last_verified, because the recorded check described the material you just replaced. Run a test to record a fresh one.
platform_id cannot be changed — a credential belongs to one platform for its whole life, and a 400 is returned rather than a rebind. Server-managed fields (the tenant binding, the stored-secret pointer, the observed-use record) cannot be set from a request body and are preserved.
Credentials with no vendor account
Some platforms publish no way to identify “your account over there”, and some credentials are not tied to a vendor at all — an internal HTTP service, a customer-specific endpoint. Those work through the same create call: supplytenant_name with a label you choose, and the credential is filed under a manual tenant of that name. There is no create-the-tenant-first step.
A manual tenant behaves like any other on /v1/tenants: it is listed, renamable and retirable. It has no provider-published rate caps, so its budget is empty, and its name is unique within your organization only.
Endpoints
All endpoints require authentication and operate only on your organization’s credentials. Cross-tenant access returns404, never 403.
Creating one — the platform, the method, and the material, and nothing else:
tenancy_id and a resolved tenant:
400when the credentials could not be verified — wrong material, or the platform could not be reached. Nothing is written. UsePOST /credentials/testfirst if you want to check without committing.409when that provider account already belongs to another GetDialed account, or when these exact credentials are already stored here. A vendor account belongs to exactly one GetDialed account platform-wide.
credentials_ref instead of credentials to point at a secret that already exists. The two are mutually exclusive, and a ref-only create performs no live verification, so its tenancy_id stays null.
Deleting a credential
Deleting removes the record, and reclaims the stored secret only when GetDialed created that secret in the first place:- A secret GetDialed created, used by nothing else — the record is deleted and the secret is scheduled for deletion with a recovery window. Nothing is destroyed immediately.
- A secret shared with other credentials in your account — the delete is refused with
409and the record is left untouched, so the same request can be retried. The message tells you how many other credentials still reference it. One secret serving several credentials is normal, not an error. - A secret GetDialed did not create (one provisioned for you directly) — the record is removed and the secret is left exactly as it was.
Testing credentials
Two endpoints check credentials against the live platform, so you catch a bad password before a flow fails at 2 a.m. Both require an admin caller.1
Test before saving
POST /credentials/test takes platform_id, auth_method and a platform-specific credentials object. It returns a verdict and persists nothing — the test-before-save flow.2
Test a saved credential
POST /credentials/{credential_id}/test (no request body) resolves the saved credential’s stored secret and runs the same check. On success, last_verified is updated. This is what you want after a rotation, or when a flow starts failing.200 with a TestResult body for every outcome — success, bad credentials, even a timeout. 4xx statuses are reserved for problems with your request itself.
What a credential can actually do
A liveness test cannot tell you what a credential is permitted to do. The record of what it has done can. Every credential carries atelemetry object keyed by service_id, stamped as work actually happens. For each service the credential has been used with it reports when it was last used, when it last succeeded, when it last failed, and what class of failure that was:
GET /credentials, so a list view can show health without a request per row and without running a flow to find out. A credential that has never been used has an empty record — which is a real answer, not a missing one.
last_error_class is a failure class, never a failure message. Provider error strings can embed request URLs carrying credential material, so they are never published here.
Discovering credential fields
To build a credential form you need to know which fields a platform requires — a username and password for Five9 basic auth, a token for another platform. The catalog service listing carries that schema.GET /catalog/platforms/{platform_id}/services returns each service with a credential_fields array:
This is metadata only — it describes what to collect, never actual credential values. A service without a published schema returns an empty
credential_fields list.
Next steps
Platform tenancy
The tenant a credential belongs to, and the budget they share.
Integration catalog
Browse the platforms and services a credential can reach.
Quota and progress
Which credential is spending a tenant’s provider budget.
Build your first flow
Put a credential to work in a multi-step flow.