Authentication & keys

One header authenticates every call — and a key pair keeps the credentials you send unreadable to everyone but us

View as Markdown

Two different things are protected here, and they use different mechanisms. Your application proves who it is with a secret in a header. The end user’s credentials, which travel inside the request, can be encrypted with a public key so that not even an intercepted body reveals them.


The application secret

Every call carries it:

X-APP-SECRET: <your application secret>

The secret belongs on your server and nowhere else. It can start executions for any of your customers and read every record extracted under your application. It must never reach a browser, a mobile binary, a front-end bundle or anything a user can open — there is no client-side use of this API, by design.

Secrets are issued per application in the console, and an application can hold more than one. That is what makes rotation uneventful: mint the new secret, deploy it, then revoke the old one. Revoking a secret never touches the application, its executions or its data.

When authentication fails, the answer is an HTTP status and a stable detail code — branch on the code, show your own message:

StatusdetailWhat happened
401not_authenticatedThe header was missing.
401invalid_application_secretThe value is not a well-formed secret.
401unauthorizedIt is well formed but unknown.
401app_secret_revokedIt was valid and has been revoked.
403app_pendingThe application exists but is not approved yet.
403app_lockedThe application has been locked. Contact support.
403app_archivedThe application has been archived.

Sandbox and production

Each application is one environment or the other, and its secret only works for its own. There is no flag on the request that switches between them: you hold two applications and two secrets.

Sandbox

Engines that answer with realistic, fabricated records. No real person and no real institution behind them, so you can run the same case a hundred times — including the ones that fail on purpose.

Production

Real engines against real institutions, with a real person’s data behind every call. Every execution costs a login against a source, so it is worth arriving here with the journey already rehearsed.

The engine catalogue is environment-aware: List Engines on a sandbox application returns the sandbox engines, and only those.

Base URLs

EnvironmentBase URL
Production, todayhttps://clients.infonite.tech/apiBuild against this one.
Production, coming soonhttps://clients.infonite.io/apiNot serving yet.

clients.infonite.io is the address this API is moving to. It is not available yet; when it is, clients.infonite.tech becomes deprecated and is retired after a migration window we will announce here and in the changelog. Nothing else changes — same paths, same secrets, same payloads — so keeping the host in configuration rather than in code is all the preparation you need.

The explorer beside each endpoint in the reference lets you pick a server, so you can try a call without leaving this documentation.


Encrypting the credentials you send

Everything below is optional — the API accepts plain JSON over TLS — and it is worth doing anyway. TLS protects the connection; this protects the value, from the moment it leaves your process until it is used inside ours.

Each application has an RSA key pair whose private half is generated inside the platform and never leaves it. You get the public key. Encrypt with it and nobody downstream can read what you sent: not an intermediary, not a proxy log, not your own process afterwards.

That asymmetry is the whole point, and it is why this is the right envelope for somebody else’s password: a shared key would mean whoever can encrypt can also decrypt, and both sides holding the same secret is exactly what you do not want here.

1

Get your public key

In the console, under your application’s keys: a PEM public key, and an X.509 certificate carrying the same key for stacks that expect one. The certificate is self-signed — it is a container with a validity window, not a chain of trust; the key inside it does the work.

2

Encrypt, and say so in the value

An encrypted value is self-describing: the scheme, ::, and the Base64 of the encrypted bytes.

PrefixSchemeUse it for
rsa::RSA, PKCS#1 v1.5One short value. A 2048-bit key caps the payload at roughly 245 bytes.
hybrid::AES-256-GCM under an RSA-wrapped keyEverything else — a whole credentials object exceeds the RSA limit immediately.
3

Send it where a plain value would go

Either field by field, or the whole object as one string. Both are accepted:

Field by field
{
"parameters": {
"username": "12345678Z",
"password": "rsa::Q2lwaGVydGV4dEJhc2U2NA=="
}
}
The whole object
{
"parameters": "hybrid::QW5FbmNyeXB0ZWRKU09OT2JqZWN0"
}

The same envelope works for Resume an Execution — which is where one-time codes travel, and where it matters most.

A payload that cannot be decrypted is rejected at the door, not halfway through. The call answers 422 naming the field that failed, with error_decrypting_value — no execution is created and nothing is charged. It is almost always one of three things: the wrong key, rsa:: on a payload too large for it, or Base64 mangled in transit.

A certificate renewal is not a key rotation

Worth knowing before it happens, because the words sound alarming and the event is not. Reissuing the certificate re-signs the same key pair: everything you have already encrypted still decrypts, and the certificate you installed keeps working until you choose to replace it. Generating a new pair is the destructive one — anything encrypted against the old public key becomes unreadable — which is why it is refused for an application that already has one.

Two key pairs exist, and confusing them costs an afternoon. The inbound pair is the one on this page: we generate it, we hold the private half, and you encrypt towards us with its public key. An outbound pair is the mirror — your own public key, whose private half we never hold — used when the platform encrypts something towards you. Different direction, different key, different problem.


Not storing the credentials at all

The most robust protection is not to hold the value. If your application has tokenization enabled, one execution can hand you a token that stands in for those credentials from then on — scoped to one engine, one customer and one set of features, revocable at any moment.


Every response carries

HeaderWhat it is for
x-request-idThe identifier of that exact call. Log it. Quote it to support and we can find the request without asking you to reproduce anything.
x-process-timeSeconds we spent processing, excluding the network. Useful for telling “they are slow” apart from “the link is slow”.
x-ratelimit-limit · x-ratelimit-remaining · x-ratelimit-resetThe ceiling in force for that endpoint, what is left of it, and when the window resets.

Read your budget from the response, do not hardcode it. A client that watches those headers keeps working when a limit is tuned; one built around numbers copied from a document does not. Exceeding a limit answers 429 with a detail naming which limit you hit, so a retry can back off against the right one.