Authentication Reference
This page is for the engineer on the other side of the call: you own the API Orki will be talking to, and you need to know precisely what arrives, what Orki expects back, and how it behaves when things go wrong.
If you're setting a credential up in the dashboard, start with Authentication for API Tools instead.
OAuth2 Client Credentials
The token request is yours, not ours
Orki does not hard-code an OAuth2 body. It stores the token request you configured and replays it. The guided form builds the standard shape:
POST /oauth/token HTTP/1.1
Host: api.your-service.example
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<your client id>&client_secret=<your client secret>&scope=<scope>
scopeis included only when you filled it in.- Switching Send credentials as to
JSONsends the same four fields as anapplication/jsonobject instead. - The method is
POST. - The request times out after 30 seconds. It is not configurable per provider.
- No response transformation is applied to a token response — JOLT specs belong to tools, not credentials.
HTTP Basic client authentication
If your token endpoint wants Authorization: Basic base64(client_id:client_secret) rather than body parameters, that works — with one caveat: Orki does not perform the Base64 encoding for you.
Store the already-encoded client_id:client_secret blob as a secret, then add a token-request header under Advanced:
Authorization: Basic {{secret.basic}}
Non-standard parameters
Anything the four standard fields can't express — audience, resource, a vendor-specific grant — goes in Custom token-request body (JSON) under Advanced. It replaces the generated body entirely, so restate everything you need:
{
"grant_type": "client_credentials",
"client_id": "{{secret.clientId}}",
"client_secret": "{{secret.clientSecret}}",
"audience": "https://api.your-service.example/"
}
{{secret.<name>}} references are substituted into the URL, headers, body and query parameters alike. An unknown name resolves to an empty string rather than failing, so check the spelling if a request goes out looking oddly blank.
What Orki reads back
Your token response must be JSON.
| Orki reads | From | Default path | If it's missing |
|---|---|---|---|
| The token | Token JSONPath | access_token | The call fails — TokenPath 'access_token' not found in token response. |
| The lifetime, in seconds | Expiry JSONPath | expires_in | Silently falls back to Fallback TTL (1800 s) |
A bare path is treated as a top-level field; data.token and $.data.token both work.
token_type is ignored. The scheme comes from your Injected value template (Bearer {{token}} by default), so if your service issues something other than a bearer token, change the template rather than the response.
These responses all work:
{ "access_token": "eyJhbGci...", "expires_in": 3600, "token_type": "Bearer" }
{ "data": { "token": "eyJhbGci...", "ttl": 900 } }
{ "access_token": "eyJhbGci..." }
The second needs Token JSONPath data.token and Expiry JSONPath data.ttl. The third gets the fallback TTL — fine if your tokens are long-lived, risky if they aren't.
Caching and refresh
Tokens are cached per tenant and per provider, encrypted at rest.
cached lifetime = max(1, expires_in − safety margin)
With the defaults, a token advertising expires_in: 3600 is cached for 3570 seconds. A concurrent burst of tool calls that all miss the cache results in one token fetch per Orki instance, not one per call.
Expect roughly one token request per lifetime per tenant — not one per conversation.
The 401 rule
If your API returns exactly 401 to a tool call, and Refresh token + retry once on a 401 is on (it is by default), Orki:
- discards the cached token,
- fetches a fresh one,
- retries the call once.
A second 401 is returned as-is. 403 does not trigger this — if you reject an expired token with 403, Orki will not refresh, and you should switch that path to 401.
Workflow steps behave identically; each step is an ordinary tool call.
How the Credential Reaches Your API
For static keys and OAuth2 tokens, the resolved value is written into a header or a query parameter immediately before the request leaves Orki:
Ordering: template resolution → credential injection → your API → JOLT
Two consequences:
- The credential overwrites any same-named header the tool template produced. A provider always wins.
- JOLT runs on the response, so no transformation can ever see or alter the credential.
For static keys the injected value is the template with {{secret.<name>}} substituted — for example {{secret.apiKey}}, or Bearer {{secret.apiKey}} if you set a prefix.
WS-Security (SOAP)
WS-Security ignores the header/query settings entirely and mutates the SOAP body. Orki parses the envelope, creates a Header element if there isn't one, and prepends a wsse:Security block.
Both SOAP versions are accepted — http://schemas.xmlsoap.org/soap/envelope/ and http://www.w3.org/2003/05/soap-envelope. Anything that isn't a SOAP envelope is rejected before the call is made.
PasswordText, with a timestamp and mustUnderstand enabled:
<soapenv:Header>
<wsse:Security
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
soapenv:mustUnderstand="1">
<wsu:Timestamp wsu:Id="TS-3f2b...">
<wsu:Created>2026-08-10T09:15:22.417Z</wsu:Created>
<wsu:Expires>2026-08-10T09:20:22.417Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="UsernameToken-9ac1...">
<wsse:Username>administrator</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">s3cret</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
PasswordDigest replaces the UsernameToken contents. Nonce and wsu:Created appear only in this mode:
<wsse:UsernameToken wsu:Id="UsernameToken-9ac1...">
<wsse:Username>bob</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">quR/EWLAV4xLf9Zqyw4pDmfV9OY=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>
<wsu:Created>2026-08-10T09:15:22.417Z</wsu:Created>
</wsse:UsernameToken>
Details your service will care about:
- Digest is
Base64(SHA-1(nonce + created + password)), per the OASIS UsernameToken profile, with a fresh 16-byte nonce per call. - The
createdvalue fed into the digest is byte-identical to the one inwsu:Created. - Timestamps are
yyyy-MM-ddTHH:mm:ss.fffZ. - Timestamp validity is clamped to 30–3600 seconds, whatever is configured.
mustUnderstand="1"is emitted in the envelope's own namespace.wsse:Securityis inserted as the first child ofHeader, ahead of any headers your template already sets.- An XML declaration on your template is preserved.
Requirements on Your Endpoint
| Requirement | Detail |
|---|---|
| Publicly resolvable domain | IP literals and localhost are rejected, as are single-label hosts like auth-internal. https://auth.example.com is fine. |
| JSON token response | XML and form-encoded token responses are not supported. |
| Responds within 30 s | Fixed timeout for token requests. |
| Reachable from Orki's egress | Same domain-approval rule as any API tool — see Creating API Tools. |
Failure Semantics
This is the fact worth knowing before you debug anything else: when a credential can't be resolved — bad client secret, unreachable token endpoint, a token path that doesn't match — the tool call fails as a generic server error from the agent's point of view. The readable reason does not appear in the conversation.
To see the actual message, open the tool's Test drawer in the dashboard, or the provider's Test button. Both surface the underlying error.
Messages you may hit:
| Message | Meaning |
|---|---|
Token endpoint returned HTTP 401: … | Your endpoint rejected the client credentials. The first 200 characters of your response body are included. |
Token endpoint returned an empty response body. | 2xx with no body. |
Token endpoint response is not valid JSON. | Content couldn't be parsed as JSON. |
TokenPath '<path>' not found in token response. | The configured path doesn't match your JSON. |
Auth provider not found | The provider was deleted while still attached to a tool. |
WS-Security requires a SOAP XML request body, but the tool has no body. | WS-Security attached to a tool with no request body. |
WS-Security requires a SOAP envelope request body (soapenv:Envelope in the SOAP 1.1 or 1.2 namespace). | The body isn't a SOAP envelope. |
A token-request timeout is reported the same way as any other auth failure — not as a tool timeout.
Business errors are different: if authentication succeeds and your API returns a 4xx, that response is passed to the agent to handle. Only credential resolution failures stop the pipeline.
Why a Provider Won't Save
Validation runs server-side and stops at the first problem.
| Code | Meaning |
|---|---|
missing_title / invalid_title | Name is required, must start with a letter or underscore, and may contain only letters, digits, _ and -. |
reserved_title | Names beginning with Internal are reserved. |
duplicate_title | Another provider in this tenant already has that name. |
missing_token_request / missing_token_request_base_url | OAuth2 without a token URL. |
invalid_token_request_base_url | Token URL is an IP, localhost, or not a valid public domain. |
missing_token_path | OAuth2 without a token JSONPath. |
missing_secrets | Static key with no secret. |
invalid_injection_value_template | A static-key template that references no {{secret.<name>}}. |
missing_ws_security_secrets | WS-Security without both a username and a password. |
invalid_ws_security_ttl | Timestamp validity outside 30–3600 seconds. |
invalid_ttl_fallback / invalid_ttl_margin | Fallback TTL below 30 s, or a negative safety margin. |
Two behaviours to be aware of if you ever drive this programmatically rather than through the form:
- Updating secrets replaces the whole set. Keys you don't send are dropped, not kept. Send
********for a key to preserve its stored value. - The test endpoint always responds
200. Success and failure both come back in the body as{ ok, expiresAt, error }— don't branch on the status code.
Next Steps
- Authentication for API Tools — setting credentials up in the dashboard
- Creating API Tools — the tools these credentials authenticate
- Building Workflows — steps inherit their tool's credential