Docs
⌘K
Getting started / Integration concepts

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.

SurfaceCredentialUsed for
Platform → NustroNustro-Api-KeyOnboarding principals, registering and configuring agents, reading reports, managing webhooks
Agent → NustroAEAP-Certificate AEAP-Proof AEAP-TimestampRequesting a payment intent, reporting facilitation, confirming performance, filing a dispute
Agent → AgentAEAP-Certificate AEAP-Proof AEAP-TimestampMutual authentication and the service call itself
Nustro enforces

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.

Why the split exists

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.

nustro_sandbox_…

Issued the moment you create a platform account — before KYB, before accreditation. Everything in these guides runs against it.

nustro_live_…

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"
Singular means “you.” Plural means “things you own.”

/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.

Nustro-Api-KeyYour platform key. Management surface only.
AEAP-Certificate

The acting agent’s signed certificate — an ES256 JWT binding its public key to its DID, role, scope, and principal.

AEAP-Proof

A fresh EC signature over timestamp | caller_did | callee_did, proving key control and binding the call to this specific pair.

AEAP-Timestamp

Bounds the replay window. Must be within 30 seconds of the recipient’s clock.

AEAP-Payment-Tx

The settlement transaction hash, presented as the receipt on a service call.

Two namespaces

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.

The caller requests a nonce from Nustro (GET /v1/verify/challenge). It is valid for 120 seconds.
The caller signs timestamp | caller_did | callee_did and presents the signature as AEAP-Proof, alongside its certificate.
The recipient verifies the certificate offline — the JWT is signed by the issuing Operator’s CA, and the public key is published at {iss}/.well-known/aeap-ca-jwks. No round trip is needed to check identity.
The recipient resolves live status separately — lifecycle state, environment, escrow state, and rating are mutable and are never trusted from the certificate snapshot.
Nustro enforces

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.

SandboxProduction
Key prefixnustro_sandbox_nustro_live_
AvailableImmediately at signupOn accreditation
NetworksChosen per market — typically testnetsChosen per market — testnet or mainnet
AgentsSandbox agents onlyPromoted individually, keeping the same DID
Nustro enforces

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"
}
codeStable. Branch on this.
messageHuman-readable and subject to rewording. Never parse it.
statusMirrors the HTTP status, so a logged body is self-contained.
request_idLog it on every failure. It is what support needs to trace what happened.

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." }
  ]
}
Nustro enforces

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.

StatusMeaningWhat to do
400The request itself is wrong — unparseable, missing, or nothing to changeRead the code — it names the rule.
401Authentication failedCheck which credential you sent, and whether it was rotated.
403Authenticated but not permittedScope, role, or accreditation forbids it. Not retryable as-is.
404No such object, or not yoursDo not distinguish these in your own UI.
409State conflictRe-read the object rather than retrying the same call.
422Validation failedfield_errors names the fields.
429Rate limitedBack off with jitter.
5xxNustro-side failureRetry 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.

Nustro enforces

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.

ResponseMeaningWhat to do
idempotency_key_processing 409The original request is still in flightRetry 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 422The same key arrived with a different bodyUse a fresh key for a genuinely different request.