Integration concepts
The primitives every other guide assumes: who authenticates as what, which headers travel on which calls, how environments are separated, and what an error looks like. Read this once.
Two credentials, never interchangeable
Nustro has exactly two authentication models. Which one applies depends on who is acting — your platform, or one of its agents.
| Surface | Credential | Used for |
|---|---|---|
| Platform → Nustro | Nustro-Api-Key | Onboarding principals, registering and configuring agents, reading reports, managing webhooks |
| Agent → Nustro | AEAP-Certificate AEAP-Proof AEAP-Timestamp | Requesting a payment intent, reporting facilitation, confirming performance, filing a dispute |
| Agent → Agent | AEAP-Certificate AEAP-Proof AEAP-Timestamp | Mutual authentication and the service call itself |
Runtime economic actions authenticate as the acting agent. A management key presented on one of them is rejected. Your platform key authenticates the platform — it must never be deployed into an agent runtime, embedded in agent code, or handed to a principal.
An agent’s obligations are its own: its rating, its escrow, its dispute record. If it acted under your platform’s credential, none of those could be attributed. The requirement is normative in AEA/P §5.6.4. Read §5.6 ↗
The management key
One key per environment, sent as Nustro-Api-Key on every management call.
Issued the moment you create a platform account — before KYB, before accreditation. Everything in these guides runs against it.
Issued when your account reaches ACCREDITED. Gates live certificates,
escrow, and disputes.
Keys are returned once, at issue, and stored as a hash. Nustro cannot recover one. If a key is lost or exposed, rotate it — rotation issues a replacement and revokes the previous key immediately, with no overlap window. Anything still presenting the old key starts failing at once, so deploy the new key before you rotate.
# Every management call carries the key in a header
curl https://api.nustro.com/v1/platform \
-H "Nustro-Api-Key: nustro_sandbox_4d7e2a1c9b03f85e"/v1/platform is the authenticated caller — no id in the path, because the
key is the identity. Plural resources (/v1/customers, /v1/principals,
/v1/agents) are collections you own, addressable by id. In this API,
customer always means your customer — the business you serve — never
your Nustro account.
Wire headers
Runtime calls carry the agent’s own credentials. Your platform does not construct these — the agent does — but you will see them in logs and error paths.
The acting agent’s signed certificate — an ES256 JWT binding its public key to its DID, role, scope, and principal.
A fresh EC signature over timestamp | caller_did | callee_did, proving key
control and binding the call to this specific pair.
Bounds the replay window. Must be within 30 seconds of the recipient’s clock.
The settlement transaction hash, presented as the receipt on a service call.
AEAP-* is the protocol’s cross-implementation wire contract — identical at
every conformant Operator, normative in AEA/P §5.6. Nustro-* is Nustro’s own
management surface and is implementation-defined. An agent certified here is
verifiable by a counterparty that has never contacted Nustro; that portability
is why the namespaces stay separate.
The challenge handshake
Before a runtime call, the caller proves it controls the key bound to its certificate.
GET /v1/verify/challenge). It is valid for 120 seconds.timestamp | caller_did | callee_did and presents the signature as AEAP-Proof, alongside its certificate.{iss}/.well-known/aeap-ca-jwks. No round trip is needed to check identity.Certificate validity and live status are two different checks. A certificate
can be cryptographically valid while the agent behind it is SUSPENDED or its
escrow is CONSTRAINED. Counterparties that skip the status call are trusting
a snapshot.
Environments
Sandbox and production are separate namespaces: separate keys, separate agents, separate networks, separate data. Certificates and status are environment-scoped.
| Sandbox | Production | |
|---|---|---|
| Key prefix | nustro_sandbox_ | nustro_live_ |
| Available | Immediately at signup | On accreditation |
| Networks | Chosen per market — typically testnets | Chosen per market — testnet or mainnet |
| Agents | Sandbox agents only | Promoted individually, keeping the same DID |
A sandbox agent can never complete a verified transaction against a production counterparty. Environment mismatch fails at facilitation, before delivery.
Promotion does not migrate agents. Your platform account is promoted once; each agent then switches environment individually, keeping its DID while the AID is re-signed. Escrow is network-specific and does not carry across environments. Note that environment and network are independent: a production agent settles on whichever network its market specifies, which may still be a testnet. See Sandbox → production.
Error model
Every error returns the same flat envelope — there is no wrapper object. Four fields are always present.
{
"code": "invalid_disputed_amount",
"message": "Disputed amount exceeds the settled amount.",
"status": 400,
"request_id": "req_7d3c91f4a2"
}Two optional members appear where they apply: field_errors on validation
failures, and detail carrying structured context.
Three statuses, three questions
400 — was the request itself well formed? 422 — were the values valid?
409 — does the object’s current state allow it? Field validation always returns
422 validation_failed with a field_errors array; 400 is reserved for a
malformed or empty request, and every illegal transition is 409.
{
"code": "validation_failed",
"message": "One or more fields are invalid.",
"status": 422,
"request_id": "req_2a80f13c9d",
"field_errors": [
{ "field": "country", "code": "invalid_format", "message": "Must be ISO 3166-1 alpha-2." }
]
}Map field_errors to your form generically, once. A handler written per field
will need a new case every time a field is added; one written against the array
will not.
| Status | Meaning | What to do |
|---|---|---|
400 | The request itself is wrong — unparseable, missing, or nothing to change | Read the code — it names the rule. |
401 | Authentication failed | Check which credential you sent, and whether it was rotated. |
403 | Authenticated but not permitted | Scope, role, or accreditation forbids it. Not retryable as-is. |
404 | No such object, or not yours | Do not distinguish these in your own UI. |
409 | State conflict | Re-read the object rather than retrying the same call. |
422 | Validation failed | field_errors names the fields. |
429 | Rate limited | Back off with jitter. |
5xx | Nustro-side failure | Retry idempotent calls with backoff — see below before retrying anything that issues a secret. |
The full code catalogue is in Errors.
Idempotency
Any call that creates something — a principal, an agent, a key, a dispute —
accepts an Idempotency-Key header. Retrying with the same key replays the
original response instead of creating a second object.
This matters most for calls that return a secret exactly once. If a key-issuing call times out after the server committed, an idempotent retry returns the original response, secret included, within the replay window. Without the header, the secret is unrecoverable and the only remedy is rotation.
| Response | Meaning | What to do |
|---|---|---|
idempotency_key_processing 409 | The original request is still in flight | Retry with the same key. This is the one 409 worth retrying — generating a new key here is how one timed-out request becomes two objects. |
idempotency_key_reused 422 | The same key arrived with a different body | Use a fresh key for a genuinely different request. |