Referência / Quadra Command API
Operator
31 operações em Quadra Command API 0.2.0.
/v1/evidence/{evidence_id}/resolveManually match an unmatched evidence to an obligation
Manual match: settle the operator-pinned obligation and unwind the suspense park this evidence created (≤ 2 Hydra txs, each leg idempotent). The evidences row flips to MANUALLY_MATCHED; a second call 409s.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| evidence_id | string | pathobrigatório |
| Idempotency-Key | string | headerobrigatório |
Corpo da requisição
EvidenceResolveRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/evidence/{evidence_id}/retryRetry a failed evidence ingest
Re-run a stored `status=ERROR` evidence row through the ingest pipeline once its cause is fixed (zero recorded effects only). A still-unfixed cause surfaces the same 4xx the original ingest returned.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| evidence_id | string | pathobrigatório |
| Idempotency-Key | string | headerobrigatório |
Corpo da requisição
EvidenceRetryRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/obligations/{obligation_id}/redispatchRe-drive a failed outbound dispatch
Re-queue a FAILED dispatch (or the enqueue-failure signature). EXCEPTION rows return to FINAL(null), `dispatch_status` becomes RETRY_REQUESTED, and a fresh `outbound_dispatch` job is enqueued for tx-worker (RFC-022 §4.2).
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| obligation_id | string | pathobrigatório |
| Idempotency-Key | string | headerobrigatório |
Corpo da requisição
ObligationRedispatchRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/obligations/{obligation_id}/resolveOperator resolve of a stuck obligation
Clear a PENDING/EXCEPTION obligation with an operator-attested outcome + proof (D1). Exactly one Hydra tx when the obligation UTxO is live (the value is disposed and the new output resolves FINAL on confirm — `hydra_tx_id` non-null); zero when spent/absent (metadata-only resolve). Accepts `obl_*` and legacy `txhash_N` ids.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| obligation_id | string | pathobrigatório |
| Idempotency-Key | string | headerobrigatório |
Corpo da requisição
ObligationResolveRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/foundry/compilesList a tenant's compile jobs
This tenant's compile jobs, newest first. Source is never echoed back.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| limit | integer | query |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | FoundryCompileListResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/foundry/compilesSubmit a product bundle to the Foundry
Compile and gate one tenant-authored validator.
**202, not 201** — the job is accepted, not finished. Poll the companion `GET` for its verdict.
A submission that fails a static gate (the source admission scan, the manifest through `load_manifest`, the namespace check) comes back **already REFUSED** and is never queued: the compile slot is the expensive thing, and source that cannot pass a lexer will not pass a compiler.
⚠️ Compiling is not certifying and certainly not binding. A green report is evidence; conformance execution, the adversarial battery and a countersigned Certified Product Artifact are what make a product real, and nothing here writes to the bindable-template allowlist.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
FoundryCompileRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 202 | FoundryCompileResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/foundry/compiles/{compile_id}Get one compile job and its gate report
One compile job, at whatever stage it has reached.
The gate report lists **every** gate that ran, passes included, plus a `deferred` block naming what this stage deliberately did not prove. A report that showed only failures could not answer "was this checked?", which is the question a countersigner asks.
⛔ A foreign tenant's `compile_id` and one that never existed are the same DynamoDB miss — the tenant is a partition-key segment, so 404-before-403 holds in its strongest form.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| compile_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | FoundryCompileResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/foundry/compiles/{compile_id}/sourceRead a submission's source (the P6 countersigner's read)
The tenant's Haskell, verbatim, for the human who has to sign off on it.
⛔ **This route exists because §4.3's countersignature is not a rubber stamp.** P6 requires a platform engineer to countersign *the report and the source*, and LaaS-88 D2 keeps the source out of the published evidence set — SSE-KMS, platform-only, referenced from the certification by hash. So there has to be exactly one way to read it, and this is it: operator-scoped, per compile, logged with the client that asked.
⚠️ **The honest cost of D2, stated where a reader meets it:** an outside auditor can verify every hash and both signatures and *cannot* check that the certified script came from the claimed source. Publishing the source would close that gap and would also publish a tenant's IP to everyone holding the evidence bucket. That trade was made deliberately.
Answers `text/plain` — the bytes as submitted, whose sha256 is the `source_hash` in the gate report.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| compile_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/product-keysRead a tenant's bundle-signing key enrollment
The active enrollment and the fingerprints it superseded, or 404.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | ProductKeyResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/product-keysEnroll a tenant's bundle-signing public key
Record the public key whose signature proves who authored a Product Bundle.
**Why this is an operator act and not self-service.** A tenant self-enrolling would make the recorded authority only as strong as the API credential that enrolled it — and outliving that credential is the whole reason a detached signature exists. The row records the enrolling client, so *who vouched that this key is the tenant's* has an answer.
Idempotent by fingerprint: the same key answers **200** with `enrolled: false`. A *different* key is refused **409** unless `replace: true`, and a rotation files the incumbent as superseded rather than overwriting it — a certification signed under the old key must keep verifying, so its PEM stays published under its own fingerprint.
Public key only. There is no field a private key could be pasted into and no code path that would know what to do with one.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
ProductKeyEnrollRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | ProductKeyResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/productsList a tenant's product bindings
This tenant's bindings, current version each, newest change first.
SUSPENDED and RETIRED products are listed. Lifecycle gates writing, never observation.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| limit | integer | query |
| cursor | string, opcional | query |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantProductListResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}Get a tenant product binding
One binding: its current version, every version recorded, and its history.
⛔ A foreign tenant's `product_id` and one that never existed are the same DynamoDB miss — the tenant is a partition-key segment, so 404-before-403 holds in its strongest form: status, body, headers and the time taken to answer cannot differ.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantProductResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}Move a product binding through its lifecycle
DRAFT → CERTIFIED → LIVE → SUSPENDED → RETIRED, on the shipped table.
**LIVE** requires `attestation.signed_by` — the platform countersignature of founder decision P6. Pre-Foundry that is a recorded operator sign-off written into the row's `certification` block; laas-86 replaces it with the certified artifact's own signature, in the same field.
**SUSPENDED** is unilateral and immediate: the next create or advance answers 409 `PRODUCT_NOT_ACCEPTING_WRITES` while a close still serves, because the adapter re-reads the binding on every write and exempts the close role. Suspending must stop new instances, never strand the ones an operator suspended it to look at.
**RETIRED** is terminal — there is no un-retire and no unbind verb at all. History stays resolvable: an obligation whose product no longer resolves is an audit hole.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Corpo da requisição
TenantProductLifecycleRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantProductResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}Bind a certified product artifact to one tenant
Bind one certified artifact to exactly one tenant.
The registry/bindings split (Product Plane §4.4): the artifact is content-addressed and published here, and the **row** is what activates it. Certification without binding deploys nothing.
For a parametric template this carries the whole job: resolve this tenant's binding params, apply them, derive the applied script hash and address the result produces, publish the applied bytes, and write the row with `applied.{template_hash, script_key, params, param_values}`. Two tenants binding one template get two addresses, and neither can be spent under the other's terms.
Idempotent on artifact identity: a repeat with a byte-identical manifest and the same baked-in params answers **200** with `bound: false` and appends no version. A changed manifest hashes differently and supersedes.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Corpo da requisição
TenantProductBindRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | TenantProductResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}/certificationRead a product's certification envelope
The signed certification, its countersignature if it has one, and staleness.
⭐ **Stale is reported, never enforced.** A battery upgrade does not invalidate a LIVE product: an inventory-hash bump would otherwise be an estate-wide outage triggered by adding an attack class, and the products it would take down are exactly the ones that passed the previous inventory honestly. The certification records `battery_version` and `inventory_hash`; this read compares them against what the estate ships now and says so. Re-certification is a deliberate act.
Everything needed to verify the artifact offline is in the response, and the published public keys live beside it in the store — `keys/product-platform-*.pub.pem` and `keys/product-countersigner-*.pub.pem`. Check it with `scripts/verify-product-certification.py`, which needs no platform access at all.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}/certificationCertify a product binding against its evidence
Assemble the **Certified Product Artifact**, sign it, and record CERTIFIED.
Staples LaaS-83's gate report, LaaS-84's conformance transcript and LaaS-85's battery report card **by reference** into one content-addressed document, captures this product's golden vectors from its own IR schemas, records which relaxation policy authorised the path, signs the body with the platform certification key and publishes the envelope to the WORM certification store.
⛔ **It verifies evidence; it does not produce evidence.** Every named document is fetched, re-hashed against the hash the request gives, and checked for an `accepted` verdict and a matching subject. A route that produced its own evidence and then judged it would be a route that certifies itself.
⛔ **Nothing is published if the evidence does not support a certification.** A refused certification is not an artifact, and the store's objects cannot be deleted.
Also the re-certification path: run against a CERTIFIED or LIVE binding it replaces the certification block and leaves the state alone, because `CERTIFIED → CERTIFIED` is not an edge in the transition table and inventing one would make the table stop describing the machine.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Corpo da requisição
TenantProductCertifyRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}/drainPlan a drain: how each open instance would be closed
Per open instance, the route that closes it — or the reason there is none.
Migration between versions is **close-and-reopen** (Product Plane §4.2 step 5): hash is address, so a new version is a new address by construction and no transaction moves value between them. This plans the closing half.
The routes come from the manifest's own close actions, walked by the same planner conformance uses — a per-product drain command would be the platform re-authoring what the tenant declared. An instance in a reachable non-terminal state with **no path out** is reported as `no_route` by name: its value is stranded, that is a manifest defect rather than an operational one, and no drain will move it.
Read-only. `POST` to the same path executes, and it needs an explicit id list.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/products/{product_id}/drainDrain a product: close the named instances
Close the instances named in `instance_ids`, through the shipped close path.
⛔ Needs `consent: true` and an explicit id list. There is no close-everything shape, because the operator reaching for a drain is usually reaching for it under time pressure and a plan they did not read is not consent.
⚠️ **Best-effort per instance, and it does not stop on the first failure.** Every close is independent, so there is nothing a rollback could mean — and a drain that aborted half-way would leave an operator with no record of which closes landed. Each instance gets its own outcome in `results`.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| product_id | string | pathobrigatório |
Corpo da requisição
TenantProductDrainRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | — | Resposta bem-sucedidaO contrato não declara um modelo para esta resposta, então a forma dela não está documentada aqui. Um roteiro de demonstração mostra a forma que ela realmente devolve. |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/provider-accountsList a tenant's processor accounts
Every processor account this tenant holds.
Never carries credential material — that crosses out once, on the call that opened the account, and is not recoverable from here or from the processor.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | ProviderAccountListResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/provider-accountsOpen a tenant's account at a processor
Open one account for this tenant at a processor, and record it.
⚠️ **The response carries the account's credential exactly once, and the platform never stores it.** A processor that mints one at creation cannot show it again — capture it from this response and provision it at the `credential.secret_ref` path, which is where the driver resolves it from. Until that is done the binding answers `credential_status: PENDING_PROVISION` and any dispatch through it fails closed with a config error rather than moving money with no credentials.
Idempotent **by the recorded account**, not by an `Idempotency-Key`: if this tenant already holds an account at this provider the processor is never called, because opening an account is *not* idempotent at the far end — a retry opens a second real account. A repeat returns the existing binding with `provisioned: false` and no credential.
A **different** account already bound for the same tenant and provider is refused with 409 rather than replaced: the first one is still open and can still receive money, so overwriting the row would orphan it silently.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
ProviderAccountRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | ProviderAccountResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/provider-accounts/{provider_id}/transfersMove value between two accounts inside one processor
Move float out of this tenant's account at `provider_id`.
⛔ **The source is the binding named in the path**, and the driver resolved from it is authenticated as that account. There is deliberately no source field on the body: a cross-tenant debit would then be one mistyped value away, and such a debit reconciles to zero on both sides, which is what makes it nearly invisible.
This is a **treasury** operation, not a customer payment. A transfer between two customers is a ledger movement on the head and involves no processor — what this moves is the fiat float backing a tenant's ledger balance, so that each processor account matches the mirror that proves it.
⚠️ `authorized: false` means the processor is holding the transfer for an out-of-band human approval. The value is queued either way; do not re-send.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| provider_id | string | pathobrigatório |
Corpo da requisição
ProviderAccountTransferRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | ProviderAccountTransferResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/qbrl-policyRead a tenant's QBRL minting policy binding
The tenant's policy binding, or 404 when it has none.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantQbrlPolicyResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/qbrl-policyBind a tenant to its own QBRL minting policy
Provision a tenant's own QBRL minting policy on a **running** head.
Under the shared-head tenancy tier a tenant's money is distinguished only by its minting policy, so this is the act of onboarding one. It generates the tenant's issuer keypair, derives the native `sig` script and its policy id, records the binding and creates the tenant's supply GL node.
Idempotent by construction — a repeat call returns the same policy id with `provisioned: false`, and no `Idempotency-Key` is required because the binding write is conditional on the policy already matching. A second, *different* policy for a tenant is refused with 409 rather than replacing the first: value already minted under the old one would still be on the head and would read as a foreign token.
This does not mint. Where the faucet's elastic supply is armed, the tenant's first funded transfer mints its opening balance under the policy bound here.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | TenantQbrlPolicyResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/quotasRead a tenant's quota and current usage
The effective quota, where it came from, and how much of today is spent.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantQuotaResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/quotasSet a tenant's quota overrides
Raise or lower what one tenant may spend on metered Foundry work.
⚠️ **Absence is the estate default, not zero.** A tenant with no row here is a tenant nobody has priced yet, which is the opposite of how an unbound QBRL policy resolves — and right for the same reason stated in reverse: there is a safe default for a compile allowance and there is none for *whose money is this*.
**The meter ships; the pricing does not.** What a compile hour is worth, and which tenants get how many, is a commercial decision owned by the founders. This route is how that decision is applied once it is made.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
TenantQuotaRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantQuotaResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/rail-bindingsList a tenant's inbound provider routes
Every provider connection whose inbound credits belong to this tenant.
An empty list is the honest answer for a tenant with no routes, and it is the state every tenant starts in: with no rows provisioned, inbound routing answers through the incumbent chain exactly as it always has.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | ProviderRouteListResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/rail-bindingsAssign a provider's inbound credits to a tenant
Record which tenant receives inbound credits through a provider.
A provider-authenticated webhook carries no bearer, so the tenant a credit belongs to cannot come from a caller — it comes from the provider binding, and until this row existed a binding could only name the environment's own tenant. Writing it is what lets a non-incumbent tenant receive money.
Idempotent — a repeat call returns the same binding with `provisioned: false`, and no `Idempotency-Key` is required because the write is conditional on the owner already matching. A **second, different** tenant for one `(rail, provider_id)` pair is refused with 409 rather than replacing the first: a provider id identifies exactly one tenant, and silently rerouting it would move a live inbound stream into another ledger. Two tenants on the same processor use two provider ids.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
ProviderRouteRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | ProviderRoute | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/transfer-templatesList a tenant's transfer templates
This tenant's templates, current version each, newest change first.
RETIRED templates are listed: retiring gates new execution, never observation.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| limit | integer | query |
| cursor | string, opcional | query |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantTemplateListResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/transfer-templatesAuthor a tenant transfer template
Author, or supersede, one tenant-scoped transfer template.
The body is validated by calling the **same** `_parse_template` the committed `transfer_templates.json` is parsed by, so a tenant-authored template is held to every invariant an operator-authored one is. Versions are immutable and content-hashed: `template_version` must be strictly greater than this tenant's current version for the id, and the new version becomes current (D2). A colliding id — a platform template's name, or a version that does not supersede — is refused here, so the ambiguity never exists at execute time.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
Corpo da requisição
TenantTemplateCreateRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 201 | TenantTemplateResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/transfer-templates/{template_id}Get a tenant transfer template
One template's current version plus every version ever recorded.
A retired version still resolves here, and deletion has no code path at all — an obligation whose `template_content_hash` no longer resolves is an audit hole.
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| template_id | string | pathobrigatório |
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantTemplateResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
/v1/operator/tenants/{tenant}/transfer-templates/{template_id}Retire a tenant transfer template
Retire the current version: no new execution, still readable.
`RETIRED` is the only accepted status — a rejected `ACTIVE` is the model's `Literal`, i.e. a 422 from FastAPI. Idempotent: a repeat answers 200 with the already-retired row. Reactivation is a new version (D2's supersede path).
Parâmetros
| Nome | Tipo | Detalhe |
|---|---|---|
| tenant | string | pathobrigatório |
| template_id | string | pathobrigatório |
Corpo da requisição
TenantTemplateStatusPatchRequest — obrigatório. Os campos estão listados em Esquemas, abaixo.
Respostas
| Status | Corpo | Detalhe |
|---|---|---|
| 200 | TenantTemplateResponse | Resposta bem-sucedida |
| 422 | HTTPValidationError | Erro de validação |
Esquemas
Os modelos que as operações acima aceitam e devolvem. Campos opcionais estão marcados como tal; um campo tipado como outro modelo está documentado sob o nome dele.
BundleSignature
The detached authorship signature that rides a bundle submission.
| Campo | Tipo | Detalhe |
|---|---|---|
| signature | string | obrigatóriobase64 DER ECDSA-P256 signature over the **32 raw bytes** of bundle_hash — prehashed, never the hash re-hashed. `openssl pkeyutl -sign -pkeyopt digest:sha256`, not `openssl dgst -sign`. |
| signing_pubkey_fpr | string | Optional echo of the key used. Compared when present and never trusted: it exists so an author who has rotated is told they signed with the superseded key instead of reading a bare verification failure.Padrão "" |
CertificationEvidence
The three documents a certification staples together by reference.
| Campo | Tipo | Detalhe |
|---|---|---|
| battery | CertificationEvidenceRef | obrigatórioLaaS-85's adversarial battery report card. Required. |
| conformance | CertificationEvidenceRef | obrigatórioLaaS-84's conformance transcript. Required. |
| gate_report | CertificationEvidenceRef, opcional | LaaS-83's compile gate report. **Optional, and its absence is recorded rather than defaulted**: a catalog product was never compiled through the Foundry — its bytes are committed and byte-gated by scripts/compile-validators.sh — so there is no gate report to name, and an empty one would be a green-looking field with nothing behind it. |
Disposition
Optional override of the D2 default destination for a live-value resolve (SETTLED → quadra-faucet; FAILED/CANCELED → the obligation's debtor).
| Campo | Tipo | Detalhe |
|---|---|---|
| destination_account_id | string | obrigatório |
EvidenceResolveRequest
``POST /v1/evidence/{evidence_id}/resolve`` — manual match: the operator pins the target obligation an unmatched settlement should credit.
| Campo | Tipo | Detalhe |
|---|---|---|
| actor | string, opcional | |
| justification | string | obrigatório |
| obligation_id | string | obrigatórioTarget obligation (obl_* or legacy txhash_N). |
| proof | ProofBlock, opcional |
EvidenceRetryRequest
``POST /v1/evidence/{evidence_id}/retry`` — re-run a stored ``status=ERROR`` evidence row through the ingest pipeline once its cause is fixed. No payload: the source_type + raw_payload come from the persisted row.
| Campo | Tipo | Detalhe |
|---|---|---|
| actor | string, opcional | |
| justification | string | obrigatório |
FoundryCompileListResponse
| Campo | Tipo | Detalhe |
|---|---|---|
| compiles | array de FoundryCompileResponse | obrigatório |
| next_cursor | string, opcional | |
| tenant | string | obrigatório |
FoundryCompileRequest
One Product Bundle submitted for compilation and gating. ⛔ **The source travels by value and the compiled bytes never do.** That is the inverse of the binding API, and deliberately so: `TenantProductBindRequest` refuses a `cbor_hex` because pre-Foundry there is no compile provenance (laas-80 D1). This endpoint is where provenance is *created*, so source is exactly what it must accept — and the thing it will not accept is somebody else's already-compiled bytes. ⚠️ Compiling does not bind. A green report is evidence; certification (laas-86) is what turns evidence into a bindable product, and nothing here writes to `script_params.PARAMETRIC_TEMPLATES`.
| Campo | Tipo | Detalhe |
|---|---|---|
| manifest | object, opcional | The product manifest. Optional only so a compile can be smoke-run without one; a submission without a manifest is gated on the source alone and the report says so. |
| product_id | string | obrigatórioNamespaced product id, `tnt.{tenant}.{name}` |
| signature | BundleSignature, opcional | Detached authorship signature over `bundle_hash` (LaaS-88 D1). **Required on the tenant-facing route** and optional on the operator one, where an operator submitting for support is attributed by its own client id instead. A signature that is supplied is verified on either route — an unverifiable one is never recorded as authorship. |
| source | string | obrigatórioThe tenant's `Tenant.Product` module, verbatim. Its header, its single export and its allowed imports are published in docs/product-author-contract.md. |
| verify_determinism | boolean | Compile twice into different build directories and compare the bytes (laas-83 D9). Doubles the job; off by default.Padrão false |
FoundryCompileResponse
A compile job, at whatever stage it has reached.
| Campo | Tipo | Detalhe |
|---|---|---|
| applied_size_bytes | integer, opcional | |
| authorship_fpr | string, opcional | Fingerprint of the enrolled key whose signature verified over `bundle_hash`. **Absent means nothing signed this submission** — an operator submitted it, attributed by `actor` instead. The two facts stay separable rather than collapsing into one field. |
| bundle_hash | string, opcional | sha256 over `quadra.product-bundle/1 ‖ tenant ‖ product_id ‖ source_hash ‖ manifest_hash`, newline-joined. This is the value an author signs, and it is recorded whether or not anything signed it. |
| compile_id | string | obrigatório |
| created_at | string, opcional | |
| gate_report | object, opcional | |
| gate_report_hash | string, opcional | |
| gate_report_key | string, opcional | Where the gate report is published in the artifact store. A certification references it by {hash, key}; before LaaS-88 nothing published it, so a Foundry-compiled product could not be certified. |
| manifest_hash | string, opcional | |
| product_id | string | obrigatório |
| script_size_budget_bytes | integer, opcional | |
| script_size_bytes | integer, opcional | |
| source_hash | string, opcional | |
| source_key | string, opcional | Where the submitted source is published (SSE-KMS, platform-only). The bytes are never echoed by this API; the P6 countersigner reads them through the operator route. |
| status | string | obrigatórioQUEUED | COMPILING | ACCEPTED | REFUSED | ERROR |
| substrate | string, opcional | |
| template_hash | string, opcional | sha256 over the compiled template's raw script bytes. NOT a Plutus script hash: an unapplied parametric template has no address until a binding applies this tenant's params (laas-79 D2). |
| tenant | string | obrigatório |
| updated_at | string, opcional |
HTTPValidationError
| Campo | Tipo | Detalhe |
|---|---|---|
| detail | array de ValidationError |
ObligationRedispatchRequest
``POST /v1/obligations/{obligation_id}/redispatch`` — re-drive a FAILED outbound dispatch (RFC-022 §4.2 RETRY_REQUESTED).
| Campo | Tipo | Detalhe |
|---|---|---|
| actor | string, opcional | |
| justification | string | obrigatório |
| reason | string, opcional |
ObligationResolveRequest
``POST /v1/obligations/{obligation_id}/resolve`` — clear a stuck PENDING/EXCEPTION obligation with an operator-attested outcome + proof.
| Campo | Tipo | Detalhe |
|---|---|---|
| actor | string, opcional | Operator identity. Token-authoritative in production; required in the body in local mode. |
| approvals | array de não declarado | |
| disposition | Disposition, opcional | |
| justification | string | obrigatório |
| outcome | string | obrigatórioSETTLED | FAILED | CANCELED |
| proof | ProofBlock | obrigatório |
ProductAccounting
The GL mapping the registry row refuses to be written without. ⛔ `gl_node_id` is not derivable and is never defaulted. Without it `entry_projector._attribute` books a product's value to the customer's own account, both legs net to zero and `_project_group` returns at `if not legs` — no entry set, no EXCEPTION marker, no metric (laas-74's second finding). ⛔ And for a **catalog** product it is not free either: `default_node_for` checks the row's node *before* its `product_id == "quadra-escrow"` literal, so the moment the incumbent has a row the row's node wins for both surfaces. A wrong value silently re-books every escrow leg in the estate, which is why the bind pins it against a shipped map instead of trusting the body.
| Campo | Tipo | Detalhe |
|---|---|---|
| custody_to_product | string | Padrão "PRODUCT_COMMIT" |
| gl_node_id | string | obrigatório |
| product_to_custody | string | Padrão "PRODUCT_RELEASE" |
| product_to_product | string | Padrão "PRODUCT_INTERNAL" |
ProductAttestation
The human countersignature that gates LIVE (founder decision P6). ⭐ Merged into the row's **existing** `certification` block rather than a field of its own — laas-74 D6 shaped it nullable from birth precisely so laas-86 writes *into* it instead of migrating it. One data shape, two eras: the certification step fills the machine half (hashes, the platform signature's fingerprint) and this fills the human half, in the same write as the state. ⛔ **`countersignature` cannot be produced by this service, and that asymmetry is the gate.** The services hold `kms:GetPublicKey` on the countersign key and no `kms:Sign`; the signature is minted repo-side by `scripts/countersign-product-certification.py` under an IAM role a task role cannot assume. A gate the platform can forge is a field.
| Campo | Tipo | Detalhe |
|---|---|---|
| algorithm | string, opcional | Declared signature algorithm; defaults to ECDSA_SHA_256 |
| battery_version | string, opcional | Retained for the pre-Foundry shape; see report_card_hash |
| countersignature | string, opcional | ⟲ LaaS-86 D3. Base64 DER ECDSA-P256 over the certification's 32-byte body hash, signed **prehashed** (`ECDSA_SHA_256`, `MessageType=DIGEST`). Required for LIVE. Produce it with scripts/countersign-product-certification.py. |
| report_card_hash | string, opcional | Retained for the pre-Foundry shape. The certification step records the real battery report card hash, so a value here is not what the gate reads. |
| signed_by | string | obrigatórioThe countersigning **role**, never a person — the countersigner IAM role ARN, or the reviewed role name it stands for. Recorded verbatim in the hashed countersignature document. |
| signing_pubkey_fpr | string, opcional | sha256 of the signer's DER SubjectPublicKeyInfo. Optional, and checked when supplied: it lets a countersigner assert which key they used rather than being told which one was trusted. |
ProductKeyEnrollRequest
Enroll (or rotate) one tenant's bundle-signing public key. ⛔ **Public key only, and the model cannot express a private one.** There is no field a well-meaning caller could paste a private key into and no code path that would know what to do with one — the same refusal-by-omission ``product_signing`` uses to make ``sign(countersigner)`` inexpressible.
| Campo | Tipo | Detalhe |
|---|---|---|
| label | string | Free-text note for whoever reads the enrollment later — which of the tenant's systems holds the private half. Recorded, never interpreted.Padrão "" |
| public_key_pem | string | obrigatórioPEM SubjectPublicKeyInfo for an ECDSA P-256 public key. Re-serialised from the parsed key before storage, so whitespace and line endings cannot produce two byte-different publications of one key. |
| replace | boolean | Rotate: supersede the active key rather than being refused as a conflict. Required for a *different* key; a repeat of the same key is idempotent without it. ⚠️ Rotation never invalidates an existing certification — the superseded PEM stays published under its own fingerprint so old signatures keep verifying.Padrão false |
ProductKeyResponse
One tenant's enrollment, as the operator surface shows it.
| Campo | Tipo | Detalhe |
|---|---|---|
| algorithm | string | Padrão "ECDSA_SHA_256" |
| curve | string | Padrão "P-256" |
| enrolled | boolean | False when the identical key was already active and nothing was written — the laas-67 `provisioned: false` shape, answered 200.Padrão true |
| enrolled_at | string, opcional | |
| enrolled_by | string, opcional | |
| label | string, opcional | |
| public_key_pem | string | obrigatório |
| published_key | string, opcional | Where the PEM is published in the certification store, so an auditor can check an authorship claim without platform access. |
| signing_pubkey_fpr | string | obrigatóriosha256 of the DER SubjectPublicKeyInfo — what a certification records. |
| status | string | obrigatórioACTIVE | SUPERSEDED |
| superseded | array de string | Fingerprints of previously enrolled keys, newest first. |
| tenant | string | obrigatório |
ProductScriptRef
Which script bytes this binding runs on. Exactly one of the two forms. ⛔ **Bytes are never accepted over the wire, and that is the design (D1).** Pre-Foundry there is no compile provenance and no adversarial battery, so an API that took a `cbor_hex` would be the thing laas-83..86 exist to gate. A template is named instead, and the name resolves only inside `shared/script_params.PARAMETRIC_TEMPLATES` — an allowlist of git-reviewed `.plutus` artifacts that `validators.yml` compiled and byte-compared. * ``template`` — a parametric template. Its params are a property of the compiled Haskell, so the allowlist carries them and the binding applies them; a ``params`` echo here must agree or the bind is refused (D2). * ``catalog`` — a **platform catalog product** (LaaS-77): an incumbent re-declared as a manifest. Address, script hash and bytes all come from the static registry, because the product already exists at an address open instances sit at. Deriving them would move it.
| Campo | Tipo | Detalhe |
|---|---|---|
| catalog | boolean | Bind an incumbent at its own unchanged static address (LaaS-77). Mutually exclusive with `template`.Padrão false |
| certification | string, opcional | ⟲ LaaS-86 D6. The body hash of an existing certification this binding INHERITS, which is what lets it be created at CERTIFIED. Not a third source of script bytes — the script still comes from `template` or `catalog`; this names the evidence. Required for a CERTIFIED bind, and therefore required in a production estate, where DRAFT is refused at the accessor. The certification must be platform-signed and must have been produced for this manifest and this template; its battery ran against another estate's applied bytes, which is why an inherited certification cannot reach LIVE until the battery is re-run here. |
| params | array de string, opcional | Optional echo of the template's binding vocabulary, in wire order. Must equal what the template declares; it exists so a caller can assert what it thinks it is baking in, never to choose it. |
| template | string, opcional | Parametric template name, from the shipped allowlist |
ProofBlock
Evidence backing an operator resolve. ``kind`` names the source (e.g. ``sponsor_statement``), ``evidence_ids`` links any ``ev_*`` rows, and ``rule`` records the disposition rule applied. Extra descriptive keys are preserved verbatim into ``resolution_metadata`` + the audit row.
| Campo | Tipo | Detalhe |
|---|---|---|
| evidence_ids | array de string | |
| kind | string | obrigatório |
| rule | string, opcional |
ProviderAccountAddress
The address a processor's onboarding form requires. Every field here is required by at least one processor and none of them has a safe default — a postal code that cannot be resolved is a 400, not a guess.
| Campo | Tipo | Detalhe |
|---|---|---|
| complement | string, opcional | |
| number | string | obrigatório |
| postal_code | string | obrigatório |
| province | string | obrigatórioNeighbourhood / bairro |
| street | string | obrigatório |
ProviderAccountCredential
⚠️ Returned exactly once, and never stored by the platform. A processor that mints an account credential at creation typically cannot show it again. It is handed over here so an operator can provision it into the secret store out of band — the platform holds a *reference* and a status, not the value. See the route's docstring.
| Campo | Tipo | Detalhe |
|---|---|---|
| secret_ref | string | obrigatórioWhere to provision it. The descriptor resolves the credential from this path. |
| value | string | obrigatórioThe credential. Capture it now; it is not recoverable. |
ProviderAccountListResponse
| Campo | Tipo | Detalhe |
|---|---|---|
| accounts | array de ProviderAccountResponse | |
| tenant | string | obrigatório |
ProviderAccountRequest
Open one account for this tenant at a processor.
| Campo | Tipo | Detalhe |
|---|---|---|
| address | ProviderAccountAddress | obrigatório |
| company_type | string, opcional | MEI | LIMITED | INDIVIDUAL | ASSOCIATION |
| string | obrigatório | |
| income_value | number | obrigatórioDeclared monthly revenue. Required by the processor and deliberately not defaulted — a made-up figure on a regulated onboarding form is a false statement. |
| legal_name | string | obrigatório |
| mobile_phone | string | obrigatório |
| provider_id | string | obrigatórioThe provider instance id, matching `^[a-z0-9][a-z0-9-]{0,31}$`. One account per tenant per provider — two tenants on the same processor need two ids. |
| rail | string | Rail family the provider serves.Padrão "PIX" |
| tax_id | string | obrigatórioCPF or CNPJ, digits only. |
ProviderAccountResponse
| Campo | Tipo | Detalhe |
|---|---|---|
| account_number | object | |
| account_status | string | obrigatórioThe processor's own status. Rarely transactable immediately — a regulated onboarding evaluation typically follows. |
| credential | ProviderAccountCredential, opcional | Present ONLY on the call that opened the account. |
| credential_ref | string, opcional | |
| credential_status | string | obrigatórioPENDING_PROVISION until an operator has stored the credential. |
| family | string | obrigatório |
| provider_account_id | string | obrigatório |
| provider_id | string | obrigatório |
| provisioned | boolean | obrigatórioFalse when this call matched an account already recorded. |
| tenant | string | obrigatório |
| wallet_id | string, opcional |
ProviderAccountTransferRequest
Move value between two accounts inside one processor.
| Campo | Tipo | Detalhe |
|---|---|---|
| amount_cents | integer | obrigatórioAmount in centavos. |
| currency | string | Padrão "BRL" |
| description | string, opcional | |
| destination_wallet_id | string | obrigatórioThe destination account's wallet handle at the processor. |
ProviderAccountTransferResponse
| Campo | Tipo | Detalhe |
|---|---|---|
| amount_cents | integer | obrigatório |
| authorized | boolean, opcional | False when the processor is holding the transfer for an out-of-band human approval. The value is queued either way — do not re-send. |
| currency | string | obrigatório |
| family | string | obrigatório |
| provider_id | string | obrigatório |
| provider_transfer_id | string, opcional | |
| tenant | string | obrigatório |
| transfer_status | string, opcional |
ProviderRoute
One `(rail, provider)` → tenant inbound routing binding.
| Campo | Tipo | Detalhe |
|---|---|---|
| bound_in_process | boolean | Whether this deployment currently has a binding for the provider. False is legal: a route may be recorded before the driver ships.Padrão false |
| caller_less_inbound | boolean | Whether this family can receive a credit with no authenticated caller. When false, every inbound path is bearer-authenticated and the tenant is already the caller's — the route is bookkeeping rather than the routing authority.Padrão false |
| family | string | obrigatórioRail family, upper-cased. |
| kind | string | `rail` for a money rail, `kyc` otherwise.Padrão "rail" |
| provider_id | string | obrigatórioThe provider instance id. |
| provisioned | boolean, opcional | True when this call created the binding, false when it already existed with the same owner. Absent on reads. |
| status | string | ACTIVE while the route resolves.Padrão "ACTIVE" |
| tenant | string | obrigatórioThe tenant whose ledger these credits belong to. |
ProviderRouteListResponse
Every inbound route one tenant owns.
| Campo | Tipo | Detalhe |
|---|---|---|
| routes | array de ProviderRoute | |
| tenant | string | obrigatório |
ProviderRouteRequest
Which provider connection an operator is assigning to a tenant.
| Campo | Tipo | Detalhe |
|---|---|---|
| provider_id | string | obrigatórioThe provider instance id, matching `^[a-z0-9][a-z0-9-]{0,31}$`. This is the *driver instance*, not the payload schema — and it identifies exactly one tenant, so two tenants on the same processor need two ids. |
| rail | string | obrigatórioRail family the provider serves — `PIX`, `ACH`, `MOCK`, or `KYC` for the identity-verification namespace. Case-insensitive. |
TenantProductBindRequest
Bind one certified artifact to exactly one tenant. The registry/bindings split (Product Plane §4.4, the KYC pattern): the artifact is content-addressed and published here, and the **row** is what activates it for this tenant. Certification without binding deploys nothing. ``manifest`` is a manifest document in exactly the form `products/manifest/document.load_manifest` parses — the same validator the committed `contracts/manifests/*.json` go through, so a body posted here is held to every invariant a shipped one is. It is deliberately a free-form object rather than a nested Pydantic schema, for laas-78 D1's reason: the grammar has one validator, and a second description of it is how two authoring paths come to diverge. ``product_id`` and ``tenant`` inside the manifest are **overwritten from the path** before validation. A body that could name its own tenant would be a body that could bind into another tenant's namespace.
| Campo | Tipo | Detalhe |
|---|---|---|
| accounting | ProductAccounting | obrigatórioThe GL mapping for this product |
| acknowledge_open_instances | boolean | ⟲ LaaS-86 D8. A version bump that moves the script address is refused while instances are open, because migration is close-and-reopen (Product Plane §4.2 step 5) — drain them first. Set this to bind anyway: the previous version stays resolvable so its instances can still be advanced and closed, but no new ones are created under it.Padrão false |
| lifecycle | "DRAFT" | "CERTIFIED" | The state to bind at. ⟲ **LaaS-86 D5 changed the default to DRAFT**: a bind used to land at CERTIFIED, so every product was born certified with no gate report, no conformance transcript and no battery card behind it. CERTIFIED now requires `script.certification` naming an existing signed certification for this manifest and template. LIVE is never a bind — it needs the countersignature and is reached with PATCH. DRAFT is refused in a production-shaped estate at the accessor, which is why a production bind inherits a certification.Padrão "DRAFT" |
| manifest | object | obrigatórioThe manifest document (manifest_version, states, actions, schemas, …) |
| script | ProductScriptRef | obrigatórioWhich script bytes to bind |
TenantProductCertifyRequest
Assemble, sign and record a certification for one binding.
| Campo | Tipo | Detalhe |
|---|---|---|
| evidence | CertificationEvidence | obrigatórioThe documents this certification is a claim about |
| justification | string | obrigatórioWhy this certification is being recorded, for the operator timeline |
TenantProductDrainRequest
Close the named instances of one product. ⛔ There is deliberately no close-everything shape. The operator reaching for a drain is usually reaching for it under time pressure, and a plan they did not read is not consent.
| Campo | Tipo | Detalhe |
|---|---|---|
| account_id | string | obrigatórioThe acting account. A close pays out, and the payee comes from this account exactly as on the ordinary close route. |
| consent | boolean | obrigatórioMust be true: a drain closes real instances and pays real value |
| instance_ids | array de string | obrigatórioThe instances to close. Read them off GET .../drain first. |
| justification | string | obrigatórioFor the operator timeline |
| reason | object, opcional | Extra body fields the product's close action declares, merged into each close. The same shape the ordinary close route takes. |
TenantProductLifecycleRequest
Move one binding through the lifecycle. The transition table lives in `shared/product_registry._LEGAL_TRANSITIONS` and is enforced at the accessor twice — in Python for the message and as a DynamoDB `ConditionExpression` so two concurrent transitions serialize. A kill switch that races is not one. ⛔ There is no `RETIRED -> anything`, and no unbind verb at all. History stays resolvable: an obligation whose product no longer resolves is an audit hole, and a suspended product's own instances are exactly what an operator suspended it to look at.
| Campo | Tipo | Detalhe |
|---|---|---|
| attestation | ProductAttestation, opcional | Required for LIVE and refused for every other target — a countersignature on a suspension would record a sign-off nobody gave. |
| justification | string | obrigatórioWhy. Recorded on the operator timeline row (laas-24's surface). |
| lifecycle | "CERTIFIED" | "LIVE" | "SUSPENDED" | "RETIRED" | obrigatórioThe target state |
TenantProductListResponse
A page of this tenant's bindings (current version each).
| Campo | Tipo | Detalhe |
|---|---|---|
| items | array de TenantProductResponse | Padrão [] |
| next_cursor | string, opcional | Opaque cursor; null when the listing is exhausted |
TenantProductResponse
One binding's current version, as the public read model.
| Campo | Tipo | Detalhe |
|---|---|---|
| accounting | object | Padrão {} |
| actions | object | Padrão {} |
| address | string | obrigatório |
| applied | object | The parametric binding: template_hash, script_key, params, param_values. Empty for a non-parametric product.Padrão {} |
| artifact_bucket | string | Padrão "" |
| artifact_hash | string | Padrão "" |
| artifact_key | string | Padrão "" |
| bound | boolean, opcional | Bind only. False when the identical artifact was already bound and no version was appended — the laas-67 `provisioned: false` shape. |
| certification | object | Padrão {} |
| created_at | string | Padrão "" |
| datum_schema | string | Padrão "" |
| gl_node_id | string, opcional | Bind only: the node provisioned |
| ir_version | integer | Padrão 1 |
| lifecycle | string | obrigatório |
| lifecycle_history | array de object, opcional | |
| operation_id | string, opcional | The operator-timeline row this call wrote |
| product_id | string | obrigatório |
| redeemer_schema | string | Padrão "" |
| release_targets | array de string | Padrão [] |
| script_hash | string | obrigatório |
| template_hash | string, opcional | Bind only: convenience echo of applied.template_hash |
| tenant | string | obrigatório |
| tier | string | Always product; the custody tier is the platform'sPadrão "product" |
| updated_at | string | Padrão "" |
| version | integer | obrigatório |
| versions | array de object, opcional | Every version ever recorded, newest first — the detail read |
TenantQbrlPolicyResponse
A tenant's QBRL minting-policy binding.
| Campo | Tipo | Detalhe |
|---|---|---|
| issuer_account_id | string | obrigatórioThe account owning the key whose hash defines the policy. It holds no value; it exists to sign mints and burns. |
| policy_id | string | obrigatórioThe native `sig` script hash that is this tenant's minting policy. Under the shared-head tenancy tier this value is what distinguishes the tenant's money on-chain. |
| provisioned | boolean, opcional | True only when this call created the binding. A repeat call returns false with the same policy id. |
| source | string | `bootstrap` for the tenant whose policy comes from genesis, `registry` for one provisioned onto a running head.Padrão "registry" |
| status | string | ACTIVE while the policy may mint.Padrão "ACTIVE" |
| supply_node | string | GL node carrying this tenant's outstanding QBRL issuance.Padrão "" |
| tenant | string | obrigatórioThe tenant this policy issues money for. |
TenantQuotaRequest
Set one tenant's metered-work allowances. ⚠️ Every field is optional and ``None`` means *leave it alone*, not *set it to zero*. A PUT that silently zeroed an unmentioned quota would make raising one cap a way to revoke another.
| Campo | Tipo | Detalhe |
|---|---|---|
| foundry_daily_compiles | integer, opcional | Compiles this tenant may submit per UTC day. 0 is a legal value and means *suspended*, which is a different fact from an absent row (nobody has priced this tenant yet, so the estate default applies). |
TenantQuotaResponse
The effective quota, where it came from, and how much of today is spent.
| Campo | Tipo | Detalhe |
|---|---|---|
| quota | integer | obrigatório |
| quota_source | string | `tenant_override` | `estate_default` — which decided `quota`.Padrão "estate_default" |
| remaining | integer | obrigatório |
| tenant | string | obrigatório |
| updated_at | string, opcional | |
| updated_by | string, opcional | |
| used | integer | obrigatório |
| window | string | Padrão "utc_day" |
| window_resets_at | string, opcional | |
| window_started_at | string, opcional |
TenantTemplateCreateRequest
Author (or supersede) one tenant-authored transfer template. ``definition`` is a template entry in **exactly** the closed form ``services/shared/transfer_templates.json`` uses — ``template_version``, ``product_id``, ``params``, ``constants``, ``source``, ``legs`` — and it is handed to ``shared.transfer_templates._parse_template`` verbatim. ⛔ Deliberately typed as a free-form object rather than a nested Pydantic schema (D1). The template grammar has exactly one validator, and every invariant that makes a template safe to execute — the four closed amount forms, exactly one residual leg, reserved leg roles, declared param types, ``MAX_BPS``, the content hash — lives inside it. A second, Pydantic-shaped description of the same grammar would be a second set of rules to keep in step, and the two authoring paths could then diverge. ``template_version`` lives inside the definition because it is one of the entry's required fields; a separate parameter would be a second source for it. It must be strictly greater than this tenant's current version for this id.
| Campo | Tipo | Detalhe |
|---|---|---|
| definition | object | obrigatórioThe template entry, in the same shape transfer_templates.json uses (template_version, product_id, params, constants, source, legs). |
| template_id | string | obrigatórioNamespaced template id, tnt.<tenant>.<name> |
TenantTemplateListResponse
A page of this tenant's templates (current version each).
| Campo | Tipo | Detalhe |
|---|---|---|
| items | array de TenantTemplateResponse | Padrão [] |
| next_cursor | string, opcional | Opaque cursor; null when the listing is exhausted |
TenantTemplateResponse
One tenant template's current version, as the public read model.
| Campo | Tipo | Detalhe |
|---|---|---|
| constants | object | Padrão {} |
| content_hash | string | obrigatório |
| created_at | string | Padrão "" |
| description | string | Padrão "" |
| legs | array de object | Padrão [] |
| params | object | Padrão {} |
| product_id | string | obrigatório |
| source | object | Padrão {} |
| status | string | ACTIVE or RETIREDPadrão "ACTIVE" |
| status_history | array de object, opcional | |
| template_id | string | obrigatório |
| template_version | integer | obrigatório |
| tier | string | platform (static) or tenant (authored)Padrão "tenant" |
| updated_at | string | Padrão "" |
| versions | array de object, opcional | Every version ever recorded, newest first — present on the single-template read. A retired version stays listed so an obligation's template_content_hash resolves forever. |
TenantTemplateStatusPatchRequest
Retire a template's current version. ⛔ ``RETIRED`` is the only accepted value, and the restriction is the design (D2: supersede, never edit; retire, never delete). Bringing a template back is a new version through ``POST``; a ``RETIRED -> ACTIVE`` flip would make "was this template live when that obligation was written" unanswerable from the version alone.
| Campo | Tipo | Detalhe |
|---|---|---|
| status | string | obrigatórioOnly RETIRED is accepted; reactivate by posting a new version |
| template_version | integer, opcional | Optional pin: the version expected to be current. A mismatch is 409, so a retire cannot land on a version the caller has not seen. |
ValidationError
| Campo | Tipo | Detalhe |
|---|---|---|
| ctx | object | |
| input | não declarado | |
| loc | array de string ou integer | obrigatório |
| msg | string | obrigatório |
| type | string | obrigatório |