{"openapi":"3.1.0","info":{"title":"Tangle Sandbox API","version":"v1","description":"Create sandboxes, run commands and agents in them, and manage your account over HTTP. Send an account API key as a bearer token."},"components":{"securitySchemes":{"E2BApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Your Tangle API key, sent the way the E2B SDK sends its key."},"SessionCookie":{"type":"apiKey","in":"cookie","name":"session","description":"Browser session set by a completed sign-in. The same value also works as a Bearer token."},"MfaChallengeCookie":{"type":"apiKey","in":"cookie","name":"mfa_challenge","description":"Set by a sign-in that still needs a second factor. It authorizes only `POST /auth/mfa/challenge`."},"BearerAuth":{"type":"http","scheme":"bearer","description":"Use an account API key from a trusted server, CI job, or CLI. Do not put account keys in browser JavaScript. See the authentication guide for scoped browser tokens."}},"schemas":{"ChargeIntentCheck":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"],"description":"degraded when the oldest due work is overdue, or any row is auth-refused, keyless on a paid plan or repeatedly deferred."},"readAgeSeconds":{"type":"number","description":"Seconds since the queue was last read."},"depth":{"type":"number","description":"Rows in the queue."},"oldestAgeSeconds":{"type":"number","description":"Age of the oldest row; 0 when the queue is empty."},"overdueDepth":{"type":"number","description":"Rows past their due time."},"overdueAgeSeconds":{"type":"number","description":"Seconds the oldest due row has waited."},"unsettleableDepth":{"type":"number","description":"Rows that cannot settle."},"authRefusedDepth":{"type":"number","description":"Rows the ledger has refused for an authorization reason for about an hour."},"serviceRefusedDepth":{"type":"number","description":"The subset of authRefusedDepth refused on the service credential rather than a payer key."},"authRefusedKeys":{"type":"number","description":"Distinct payer credentials among the rows refused on a payer credential."},"keylessPaidDepth":{"type":"number","description":"Pro and Enterprise compute rows without a payer key; Free rows are excluded."},"budgetRefusedDepth":{"type":"number","description":"Rows waiting on a spent key budget until their attempts run out, then set aside."},"deferredDepth":{"type":"number","description":"Rows the ledger has deferred repeatedly."},"keyRevokedDepth":{"type":"number","description":"Rows refused because the payer's key was revoked; set aside at the first refusal."},"budgetClosedDepth":{"type":"number","description":"Rows refused on a key budget that can never be raised; set aside at the first refusal."}},"required":["status","readAgeSeconds","depth","oldestAgeSeconds","overdueDepth","overdueAgeSeconds","unsettleableDepth","authRefusedDepth","serviceRefusedDepth","authRefusedKeys","keylessPaidDepth","budgetRefusedDepth","deferredDepth","keyRevokedDepth","budgetClosedDepth"]},"E2BSandbox":{"type":"object","properties":{"alias":{"type":"string"},"clientID":{"type":"string"},"domain":{"type":["string","null"]},"envdAccessToken":{"type":"string"},"envdVersion":{"type":"string"},"sandboxID":{"type":"string"},"templateID":{"type":"string"},"trafficAccessToken":{"type":["string","null"]}},"required":["clientID","envdVersion","sandboxID","templateID"]},"E2BError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"number"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"E2BNewSandbox":{"type":"object","properties":{"templateID":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"envVars":{"type":"object","additionalProperties":{"type":"string"}},"timeout":{"type":"number"},"secure":{"type":"boolean"},"allow_internet_access":{"type":"boolean"},"autoPause":{"type":"boolean"},"autoResume":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}},"required":["templateID"]},"E2BListedSandbox":{"type":"object","properties":{"alias":{"type":"string"},"clientID":{"type":"string"},"cpuCount":{"type":"number"},"diskSizeMB":{"type":"number"},"endAt":{"type":"string"},"envdVersion":{"type":"string"},"memoryMB":{"type":"number"},"metadata":{"type":"object","additionalProperties":{"type":"string"}},"sandboxID":{"type":"string"},"startedAt":{"type":"string"},"state":{"type":"string","enum":["running","paused"]},"templateID":{"type":"string"}},"required":["clientID","cpuCount","diskSizeMB","endAt","envdVersion","memoryMB","sandboxID","startedAt","state","templateID"]},"E2BSandboxDetail":{"allOf":[{"$ref":"#/components/schemas/E2BListedSandbox"},{"type":"object","properties":{"allowInternetAccess":{"type":["boolean","null"]},"domain":{"type":["string","null"]},"envdAccessToken":{"type":"string"}}}]},"E2BProcess":{"type":"object","properties":{"pid":{"type":"string"},"command":{"type":"string"},"status":{"type":"string","enum":["running","exited"]},"exitCode":{"type":["number","null"]},"stdout":{"type":"string"},"stderr":{"type":"string"},"startedAt":{"type":"string"},"finishedAt":{"type":["string","null"]}},"required":["pid","command","status","exitCode","stdout","stderr","startedAt","finishedAt"]},"E2BProcessStart":{"type":"object","properties":{"cmd":{"type":"string","description":"Runs as `/bin/sh -c <cmd>`."},"cwd":{"type":"string"},"envs":{"type":"object","additionalProperties":{"type":"string"}},"timeout":{"type":"number","description":"Milliseconds."}},"required":["cmd"]},"E2BTemplate":{"type":"object","properties":{"templateID":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"cpuCount":{"type":"number"},"memoryMB":{"type":"number"},"public":{"type":"boolean"}},"required":["templateID","cpuCount","memoryMB","public"]},"CredentialRefusal":{"type":"object","properties":{"error":{"type":"string","description":"`unauthorized`, `invalid_delegated_key`, `invalid_fleet_token` or `invalid_platform_key` on a 401; key verification also answers `email_verification_required` or `platform_unavailable`."},"message":{"type":"string"},"code":{"type":"string"},"invalidReason":{"type":"string"},"reason":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["error"]},"ErrorResponse":{"type":"object","properties":{"error":{"anyOf":[{"type":"string"},{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}]},"code":{"type":"string"},"restartKind":{"type":"string","enum":["rollout","maintenance","authorized","in_place"]},"detail":{"type":"string"},"phase":{"type":"string"}},"required":["error"]},"FailureEnvelope":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},"DurablePlan":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"},"sourceTurnId":{"type":"string"},"status":{"type":"string","enum":["pending"]}},"required":["id","revision","body","submittedAt","sourceTurnId","status"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"},"sourceTurnId":{"type":"string"},"status":{"type":"string","enum":["approved"]},"decidedAt":{"type":"string"}},"required":["id","revision","body","submittedAt","sourceTurnId","status","decidedAt"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"},"sourceTurnId":{"type":"string"},"status":{"type":"string","enum":["rejected"]},"decidedAt":{"type":"string"},"feedback":{"type":"string"}},"required":["id","revision","body","submittedAt","sourceTurnId","status","decidedAt","feedback"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"},"sourceTurnId":{"type":"string"},"status":{"type":"string","enum":["superseded"]},"supersededAt":{"type":"string"},"supersededByPlanId":{"type":"string"}},"required":["id","revision","body","submittedAt","sourceTurnId","status","supersededAt","supersededByPlanId"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"},"sourceTurnId":{"type":"string"},"status":{"type":"string","enum":["withdrawn"]},"withdrawnAt":{"type":"string"},"reason":{"type":"string"}},"required":["id","revision","body","submittedAt","sourceTurnId","status","withdrawnAt","reason"],"additionalProperties":false}]},"SandboxCreateResponse":{"type":"object","properties":{"proxySecretReceipt":{"oneOf":[{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["proxy-installed"]},"materializationAcknowledged":{"type":"boolean","enum":[false]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["installed"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["revoked"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false}]},"id":{"type":"string"},"name":{"type":"string"},"filesystemIncarnationId":{"type":"string","format":"uuid"},"filesystemIncarnationProvenance":{"type":"string","enum":["fresh","restored","unknown"]},"filesystemIncarnationReadiness":{"type":"string","enum":["transitioning","ready"]},"status":{"type":"string","enum":["pending","provisioning","running","stopped","failed","expired","deleted"]},"connection":{"type":"object","properties":{"runtimeUrl":{"type":"string"},"authToken":{"type":"string"},"runtimeAccessToken":{"type":"string"},"authTokenExpiresAt":{"type":"string"},"edgeStatus":{"type":"string","enum":["pending","ready","failed","skipped"]},"edgeReadyAt":{"type":"string"},"edgeError":{"type":"string"},"ssh":{"type":"object","properties":{"username":{"type":"string"},"port":{"type":"number"},"proxyCommand":{"type":"string"}},"required":["username","port","proxyCommand"]},"webTerminalUrl":{"type":"string"}}},"metadata":{"type":"object","additionalProperties":{}},"captureProof":{"type":"object","properties":{"hostId":{"type":"string","minLength":1},"containerId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"imageId":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"bundleRevision":{"type":"string","pattern":"^[a-f0-9]{40}$"},"bundleChecksum":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}},"required":["hostId","containerId","imageId","bundleRevision","bundleChecksum"],"additionalProperties":false},"createdAt":{"type":"string"},"startedAt":{"type":"string"},"lastActivityAt":{"type":"string"},"expiresAt":{"type":"string"},"lifecycleState":{"type":"string","enum":["suspending","suspended","deleting"]},"generation":{"type":"string"},"suspendedAt":{"type":"string"},"error":{"type":"string"},"capabilities":{"type":"array","items":{"type":"string","enum":["computer_use"]}},"compliance":{"type":"object","properties":{"hipaa":{"type":"boolean","enum":[true]}},"required":["hipaa"],"additionalProperties":false},"teamId":{"type":"string"},"teamRole":{"type":"string","enum":["owner","admin","member","viewer"]},"egressPolicy":{"type":"object","properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false},"egressPolicySource":{"type":"string","enum":["sandbox","team","platform"]},"funding":{"type":"string","enum":["wallet","referral_credit"]},"stopReason":{"type":"string","enum":["budget_exhausted","key_revoked","insufficient_balance","scoped_credit_exhausted","scoped_credit_held"]},"gpuLease":{"type":"object","properties":{"id":{"type":"string","minLength":1},"provider":{"type":"string","minLength":1},"providerLeaseId":{"type":"string"},"providerInstanceType":{"type":"string"},"status":{"type":"string"},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1},"count":{"type":"integer","exclusiveMinimum":0}},"required":["kind","count"]},"billing":{"type":"object","properties":{"seconds":{"type":"integer","minimum":0},"customerCostUsd":{"type":"number","minimum":0},"providerCostUsd":{"type":"number","minimum":0},"startedAt":{"type":"string","minLength":1},"stoppedAt":{"type":"string","minLength":1}},"required":["seconds","customerCostUsd","providerCostUsd","startedAt","stoppedAt"]}},"required":["id","provider","status","accelerator"],"additionalProperties":{}},"directRead":{"type":"object","properties":{"url":{"type":"string"},"token":{"type":"string"},"expiresAt":{"type":"number"},"observed":{"type":"string"}},"required":["url","token","expiresAt","observed"]},"resources":{"$ref":"#/components/schemas/SandboxShape"},"end":{"type":"object","properties":{"reason":{"type":"string"},"snapshotId":{"type":["string","null"]},"requester":{"type":["string","null"]},"endedAt":{"type":"string"},"finalizedAt":{"type":["string","null"]},"runtimeReady":{"type":"boolean"},"runs":{"type":"array","items":{"type":"object","properties":{"runId":{"type":"string"},"startedAt":{"type":"number"},"endedAt":{"type":["number","null"]},"closedReason":{"type":["string","null"]},"billedMs":{"type":"number"},"computeUsd":{"type":["number","null"]}},"required":["runId","startedAt","endedAt","closedReason","billedMs","computeUsd"]}}},"required":["reason","snapshotId","requester","endedAt","finalizedAt","runtimeReady","runs"]},"createReceipt":{"$ref":"#/components/schemas/SandboxCreateReceipt"},"backend":{"type":"object","properties":{"type":{"type":"string"},"status":{"type":"string","enum":["unknown"]}},"required":["type","status"],"additionalProperties":false}},"required":["id","status","createdAt","capabilities","createReceipt"],"additionalProperties":false},"SandboxShape":{"type":"object","properties":{"cpuCores":{"type":"number"},"memoryMB":{"type":"number"},"diskGB":{"type":"number"},"pool":{"type":"string"},"ttlMs":{"type":"number"}},"required":["cpuCores","memoryMB","diskGB"]},"SandboxCreateReceipt":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","idempotent_replay","unknown"]},"idempotencyKeyApplied":{"type":"boolean"},"ownerContext":{"type":"string","enum":["inherit","isolated"]},"injectedSecrets":{"type":"array","items":{"type":"string"}},"unboundSecretDeliveries":{"type":"integer","minimum":0},"captureProof":{"type":"object","properties":{"hostId":{"type":"string","minLength":1},"containerId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"imageId":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"bundleRevision":{"type":"string","pattern":"^[a-f0-9]{40}$"},"bundleChecksum":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}},"required":["hostId","containerId","imageId","bundleRevision","bundleChecksum"],"additionalProperties":false}},"required":["outcome","idempotencyKeyApplied"],"additionalProperties":false},"RestoreQuotaErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["RESTORE_QUOTA_EXCEEDED"]},"quotaType":{"type":"string","enum":["restore"]},"snapshotBytes":{"type":"integer","minimum":0},"quotaBytes":{"type":"integer","minimum":0}},"required":["error","code","quotaType"]},"SandboxResponse":{"type":"object","properties":{"proxySecretReceipt":{"oneOf":[{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["proxy-installed"]},"materializationAcknowledged":{"type":"boolean","enum":[false]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["installed"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["revoked"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false}]},"id":{"type":"string"},"name":{"type":"string"},"filesystemIncarnationId":{"type":"string","format":"uuid"},"filesystemIncarnationProvenance":{"type":"string","enum":["fresh","restored","unknown"]},"filesystemIncarnationReadiness":{"type":"string","enum":["transitioning","ready"]},"status":{"type":"string","enum":["pending","provisioning","running","stopped","failed","expired","deleted"]},"connection":{"type":"object","properties":{"runtimeUrl":{"type":"string"},"authToken":{"type":"string"},"runtimeAccessToken":{"type":"string"},"authTokenExpiresAt":{"type":"string"},"edgeStatus":{"type":"string","enum":["pending","ready","failed","skipped"]},"edgeReadyAt":{"type":"string"},"edgeError":{"type":"string"},"ssh":{"type":"object","properties":{"username":{"type":"string"},"port":{"type":"number"},"proxyCommand":{"type":"string"}},"required":["username","port","proxyCommand"]},"webTerminalUrl":{"type":"string"}}},"metadata":{"type":"object","additionalProperties":{}},"captureProof":{"type":"object","properties":{"hostId":{"type":"string","minLength":1},"containerId":{"type":"string","pattern":"^[a-f0-9]{64}$"},"imageId":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"bundleRevision":{"type":"string","pattern":"^[a-f0-9]{40}$"},"bundleChecksum":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"}},"required":["hostId","containerId","imageId","bundleRevision","bundleChecksum"],"additionalProperties":false},"createdAt":{"type":"string"},"startedAt":{"type":"string"},"lastActivityAt":{"type":"string"},"expiresAt":{"type":"string"},"lifecycleState":{"type":"string","enum":["suspending","suspended","deleting"]},"generation":{"type":"string"},"suspendedAt":{"type":"string"},"error":{"type":"string"},"capabilities":{"type":"array","items":{"type":"string","enum":["computer_use"]}},"compliance":{"type":"object","properties":{"hipaa":{"type":"boolean","enum":[true]}},"required":["hipaa"],"additionalProperties":false},"teamId":{"type":"string"},"teamRole":{"type":"string","enum":["owner","admin","member","viewer"]},"egressPolicy":{"type":"object","properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false},"egressPolicySource":{"type":"string","enum":["sandbox","team","platform"]},"funding":{"type":"string","enum":["wallet","referral_credit"]},"stopReason":{"type":"string","enum":["budget_exhausted","key_revoked","insufficient_balance","scoped_credit_exhausted","scoped_credit_held"]},"gpuLease":{"type":"object","properties":{"id":{"type":"string","minLength":1},"provider":{"type":"string","minLength":1},"providerLeaseId":{"type":"string"},"providerInstanceType":{"type":"string"},"status":{"type":"string"},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1},"count":{"type":"integer","exclusiveMinimum":0}},"required":["kind","count"]},"billing":{"type":"object","properties":{"seconds":{"type":"integer","minimum":0},"customerCostUsd":{"type":"number","minimum":0},"providerCostUsd":{"type":"number","minimum":0},"startedAt":{"type":"string","minLength":1},"stoppedAt":{"type":"string","minLength":1}},"required":["seconds","customerCostUsd","providerCostUsd","startedAt","stoppedAt"]}},"required":["id","provider","status","accelerator"],"additionalProperties":{}},"directRead":{"type":"object","properties":{"url":{"type":"string"},"token":{"type":"string"},"expiresAt":{"type":"number"},"observed":{"type":"string"}},"required":["url","token","expiresAt","observed"]},"resources":{"$ref":"#/components/schemas/SandboxShape"},"end":{"type":"object","properties":{"reason":{"type":"string"},"snapshotId":{"type":["string","null"]},"requester":{"type":["string","null"]},"endedAt":{"type":"string"},"finalizedAt":{"type":["string","null"]},"runtimeReady":{"type":"boolean"},"runs":{"type":"array","items":{"type":"object","properties":{"runId":{"type":"string"},"startedAt":{"type":"number"},"endedAt":{"type":["number","null"]},"closedReason":{"type":["string","null"]},"billedMs":{"type":"number"},"computeUsd":{"type":["number","null"]}},"required":["runId","startedAt","endedAt","closedReason","billedMs","computeUsd"]}}},"required":["reason","snapshotId","requester","endedAt","finalizedAt","runtimeReady","runs"]}},"required":["id","status","createdAt","capabilities"],"additionalProperties":false},"DeleteMatchingSandboxesResponse":{"type":"object","properties":{"matched":{"type":"integer","minimum":0},"remaining":{"type":"integer","minimum":0},"next":{"type":"string"},"listingComplete":{"type":"boolean"},"complete":{"type":"boolean"},"sandboxes":{"type":"array","items":{"$ref":"#/components/schemas/DeleteMatchingSandboxResult"}}},"required":["matched","remaining","listingComplete","complete","sandboxes"]},"DeleteMatchingSandboxResult":{"type":"object","properties":{"id":{"type":"string"},"confirmed":{"type":"boolean"},"outcome":{"type":"string","enum":["destroyed","already_absent","failed"]},"removal":{"type":"string","enum":["pending","complete"]},"deletedChildren":{"type":"array","items":{"type":"string"}},"settlementDeferred":{"type":"boolean"},"status":{"type":"integer"},"code":{"type":"string"},"error":{"type":"string"},"children":{"type":"array","items":{"type":"string"}}},"required":["id","confirmed","outcome"]},"DeleteMatchingSandboxesRequest":{"type":"object","properties":{"metadataPrefix":{"type":"object","additionalProperties":{"type":"string","minLength":1,"maxLength":512},"description":"Metadata key to required value prefix. A sandbox matches when every named key holds a string value that starts with its prefix.","example":{"retainedIdempotencyKey":"runtime:run_123:"}},"cascade":{"type":"boolean"},"until":{"type":"string","enum":["removed"]},"after":{"type":"string","minLength":1,"maxLength":256}},"required":["metadataPrefix"],"additionalProperties":false},"SandboxEgressUsage":{"type":"object","properties":{"sandboxId":{"type":"string"},"month":{"type":"string"},"plan":{"type":"string","enum":["free","pro","enterprise"]},"bytes":{"type":"integer","minimum":0},"chargeUsd":{"type":"number","minimum":0},"usdPerGb":{"type":"number","minimum":0},"accountBytes":{"type":"integer","minimum":0},"accountIncludedBytes":{"type":"integer","minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free."},"accountOverageBytes":{"type":"integer","minimum":0,"description":"The account's bytes beyond its allowance; 0 on Free."},"includedBytes":{"type":["integer","null"],"minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free.","deprecated":true},"overageBytes":{"type":"integer","minimum":0,"description":"The account's bytes beyond its allowance; 0 on Free.","deprecated":true},"accountCapBytes":{"type":["integer","null"],"minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free.","deprecated":true}},"required":["sandboxId","month","plan","bytes","chargeUsd","usdPerGb","accountBytes","accountIncludedBytes","accountOverageBytes","includedBytes","overageBytes","accountCapBytes"]},"DriverInfo":{"type":"object","properties":{"type":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"available":{"type":"boolean"},"version":{"type":"string"},"capabilities":{"type":"object","properties":{"criu":{"type":"boolean"},"gpu":{"type":"boolean"},"vsock":{"type":"boolean"},"snapshots":{"type":"boolean"},"userManagement":{"type":"boolean"},"networkIsolation":{"type":"boolean"}},"required":["criu","gpu","vsock","snapshots","userManagement","networkIsolation"]},"hosts":{"type":"number"},"capacity":{"type":"object","properties":{"available":{"type":"number"},"total":{"type":"number"}},"required":["available","total"]}},"required":["type","available","version","capabilities"],"additionalProperties":false},"SandboxOperationAccepted":{"type":"object","properties":{"operation":{"$ref":"#/components/schemas/SandboxOperation"}},"required":["operation"]},"SandboxOperation":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["stop","snapshot","delete"]},"sandboxId":{"type":"string"},"state":{"type":"string","enum":["accepted","running","succeeded","failed"]},"phase":{"type":"string"},"acceptedAt":{"type":"string"},"completedAt":{"type":"string"},"statusUrl":{"type":"string"},"result":{"type":"object","additionalProperties":{}},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["id","kind","sandboxId","state","acceptedAt","statusUrl"]},"BranchResponse":{"type":"object","properties":{"children":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string"}},"required":["id","sandboxId","status"],"additionalProperties":{}}},"materializeTimeMs":{"type":"number"},"fork":{"type":"object","properties":{"requestedCount":{"type":"integer"},"materializedCount":{"type":"integer"},"complete":{"type":"boolean"}},"required":["requestedCount","materializedCount","complete"]},"idempotency":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","replayed"]},"requestDigest":{"type":"string"}},"required":["outcome","requestDigest"]}},"required":["children","fork"]},"WorkspaceOperationConflict":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["IDEMPOTENCY_KEY_CONFLICT"]},"existingRequestDigest":{"type":"string"}},"required":["error","code","existingRequestDigest"]},"ResumeRequestRefusal":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"rejectedEnvKeys":{"type":"array","items":{"type":"string"},"description":"With code RESUME_ENV_KEY_NOT_ACCEPTED: the supplied env keys this sandbox does not take on resume."}}}]},"ResumeRefusal":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"startupDiagnostics":{"type":"object","properties":{"operations":{"type":"array","items":{"type":"object","properties":{"operation":{"type":"string"},"service":{"type":"string"},"status":{"type":"string"},"durationMs":{"type":"number"},"parent":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["operation","service","status"]}},"requestId":{"type":"string"}},"required":["operations"]},"missingEnvKeys":{"type":"array","items":{"type":"string"},"description":"With code RESUME_ENV_REQUIRED: the env keys to pass on the next resume."},"reason":{"type":"string","description":"With code RESUME_RESTORE_REFUSED: why the workspace could not be restored, such as workspace_restore_missing or workspace_restore_quota_exceeded."},"retryable":{"type":"boolean","description":"With code RESUME_RESTORE_REFUSED: whether another resume can succeed without operator action. The sandbox stays stopped and keeps its snapshot either way."}}}]},"SnapshotResponse":{"type":"object","properties":{"snapshotId":{"type":"string"},"createdAt":{"type":"string"},"sizeBytes":{"type":"number"},"tags":{"type":"array","items":{"type":"string"}},"remoteSync":{"$ref":"#/components/schemas/SnapshotRemoteSync"},"idempotency":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","replayed"]},"requestDigest":{"type":"string"}},"required":["outcome","requestDigest"]}},"required":["snapshotId","createdAt","tags"]},"SnapshotRemoteSync":{"type":"string","enum":["pending","synced","failed"]},"SnapshotListResponse":{"type":"object","properties":{"source":{"type":"string","enum":["primary","customer","primary-fallback"],"description":"Repository serving this listing. primary-fallback means customer listing failed and primary history may be incomplete."},"snapshots":{"type":"array","items":{"type":"object","properties":{"snapshotId":{"type":"string"},"projectRef":{"type":"string"},"createdAt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"paths":{"type":"array","items":{"type":"string"}},"sizeBytes":{"type":"number"},"remoteSync":{"$ref":"#/components/schemas/SnapshotRemoteSync"},"final":{"type":"boolean","enum":[true],"description":"The snapshot the sandbox's last stop captured, listed for a deleted sandbox."}},"required":["snapshotId","projectRef","createdAt","tags"]}},"deleted":{"type":"boolean","enum":[true],"description":"The sandbox is deleted: the listing holds only its retained final snapshot, which a create with fromSnapshot and fromSandboxId restores."}},"required":["snapshots"]},"SnapshotRestoreAcceptedResponse":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed"]},"progress":{"type":"number"},"result":{"type":"object","properties":{"success":{"type":"boolean"},"snapshotId":{"type":"string"},"error":{"type":"string"},"durationMs":{"type":"number"},"snapshot":{"type":"object","properties":{"snapshotId":{"type":"string"},"createdAt":{"type":"string"},"sizeBytes":{"type":"number"},"tags":{"type":"array","items":{"type":"string"}}},"required":["snapshotId","createdAt","tags"]},"phaseMs":{"type":"object","additionalProperties":{"type":"number"}}},"required":["success"]},"createdAt":{"type":"string"},"completedAt":{"type":"string"},"sandbox":{"$ref":"#/components/schemas/SandboxResponse"}},"required":["jobId","status"]},"SnapshotRestoreJobStatusResponse":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed"]},"progress":{"type":"number"},"result":{"type":"object","properties":{"success":{"type":"boolean"},"snapshotId":{"type":"string"},"error":{"type":"string"},"durationMs":{"type":"number"},"snapshot":{"type":"object","properties":{"snapshotId":{"type":"string"},"createdAt":{"type":"string"},"sizeBytes":{"type":"number"},"tags":{"type":"array","items":{"type":"string"}}},"required":["snapshotId","createdAt","tags"]},"phaseMs":{"type":"object","additionalProperties":{"type":"number"}}},"required":["success"]},"createdAt":{"type":"string"},"completedAt":{"type":"string"},"sandbox":{"$ref":"#/components/schemas/SandboxResponse"}},"required":["jobId","status","createdAt"]},"WorkspaceOperationLookup":{"type":"object","properties":{"outcome":{"type":"string","enum":["found","not_found","conflict","unknown"]},"kind":{"type":"string","enum":["checkpoint","fork"]},"state":{"type":"string","enum":["pending","succeeded","failed"]},"requestDigest":{"type":"string"},"existingRequestDigest":{"type":"string"},"result":{"type":"object","additionalProperties":{}},"failure":{"type":"object","properties":{"status":{"type":"integer"},"error":{"type":"string"},"code":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["status","error"]}},"required":["outcome","kind"]},"BatchErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"message":{"type":"string"}}}]},"GpuLease":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"image":{"type":"string","minLength":1,"maxLength":512},"diskGb":{"type":"integer","exclusiveMinimum":0},"volumeGb":{"type":"integer","minimum":0},"volumeMountPath":{"type":"string","maxLength":2048},"region":{"type":"string","minLength":1,"maxLength":128},"manifest":{"type":"object","additionalProperties":{}},"command":{"type":"array","items":{"type":"string","maxLength":8192},"minItems":1,"maxItems":256},"ports":{"type":"array","items":{"type":"integer","minimum":1,"maximum":65535},"maxItems":32},"endpoint":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"healthPath":{"type":"string","maxLength":2048,"pattern":"^\\/(?![/\\\\])"}},"required":["port"],"additionalProperties":false},"allowSpot":{"type":"boolean"},"maxHourlyUsd":{"type":"number","exclusiveMinimum":0},"weights":{"type":"object","properties":{"manifestSha":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sizeBytes":{"type":"integer","exclusiveMinimum":0}},"required":["manifestSha","sizeBytes"],"additionalProperties":false},"arms":{"type":"array","items":{},"minItems":1,"maxItems":256},"maxSpendUsd":{"type":"number","exclusiveMinimum":0,"maximum":10000},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":86400},"idleTimeoutSeconds":{"type":"integer","exclusiveMinimum":0},"id":{"type":"string"},"projectRef":{"type":"string"},"ownerUserId":{"type":"string"},"productId":{"type":"string"},"providerLeaseId":{"type":"string"},"providerInstanceType":{"type":"string"},"workerUrl":{"type":"string"},"endpointUrl":{"type":"string"},"status":{"type":"string","enum":["provisioning","ready","detaching","destroyed","failed"]},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"count":{"type":"integer","exclusiveMinimum":0,"default":1},"vramGb":{"type":"number","exclusiveMinimum":0}},"required":["kind"],"additionalProperties":false},"providerCostPerHourUsd":{"type":"number"},"customerPricePerHourUsd":{"type":"number"},"estimatedProviderCostUsd":{"type":"number"},"estimatedCustomerCostUsd":{"type":"number"},"providerSelection":{"type":"object","properties":{"strategy":{"type":"string","enum":["explicit","cheapest","ranked"]},"selectedProvider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"requestedProvider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"candidates":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"count":{"type":"integer","exclusiveMinimum":0,"default":1},"vramGb":{"type":"number","exclusiveMinimum":0}},"required":["kind"],"additionalProperties":false},"providerInstanceType":{"type":"string"},"region":{"type":"string"},"pricingSource":{"type":"string","enum":["live","configured"]},"providerCostPerHourUsd":{"type":"number"},"customerPricePerHourUsd":{"type":"number"},"maxLifetimeSeconds":{"type":"number"},"estimatedProviderCostUsd":{"type":"number"},"estimatedCustomerCostUsd":{"type":"number"}},"required":["provider","accelerator","providerCostPerHourUsd","customerPricePerHourUsd","maxLifetimeSeconds","estimatedProviderCostUsd","estimatedCustomerCostUsd"]}},"rejected":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"error":{"type":"string"}},"required":["provider","error"]}}},"required":["strategy","selectedProvider","candidates","rejected"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"readyAt":{"type":"string"},"startedAt":{"type":"string"},"lastUsedAt":{"type":"string"},"destroyedAt":{"type":"string"},"failure":{"type":"string"},"billing":{"type":"object","properties":{"seconds":{"type":"number"},"providerCostUsd":{"type":"number"},"customerCostUsd":{"type":"number"},"providerCostPerHourUsd":{"type":"number"},"customerPricePerHourUsd":{"type":"number"},"startedAt":{"type":"string"},"stoppedAt":{"type":"string"}},"required":["seconds","providerCostUsd","customerCostUsd","providerCostPerHourUsd","customerPricePerHourUsd","startedAt","stoppedAt"]}},"required":["provider","maxSpendUsd","maxLifetimeSeconds","id","projectRef","ownerUserId","status","accelerator","createdAt","updatedAt"]},"AttachGpuLeaseRequest":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"image":{"type":"string","minLength":1,"maxLength":512},"env":{"type":"object","additionalProperties":{"type":"string"}},"diskGb":{"type":"integer","exclusiveMinimum":0},"volumeGb":{"type":"integer","minimum":0},"volumeMountPath":{"type":"string","maxLength":2048},"region":{"type":"string","minLength":1,"maxLength":128},"manifest":{"type":"object","additionalProperties":{}},"command":{"type":"array","items":{"type":"string","maxLength":8192},"minItems":1,"maxItems":256},"ports":{"type":"array","items":{"type":"integer","minimum":1,"maximum":65535},"maxItems":32},"endpoint":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"healthPath":{"type":"string","maxLength":2048,"pattern":"^\\/(?![/\\\\])"}},"required":["port"],"additionalProperties":false},"allowSpot":{"type":"boolean"},"maxHourlyUsd":{"type":"number","exclusiveMinimum":0},"weights":{"type":"object","properties":{"manifestSha":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sizeBytes":{"type":"integer","exclusiveMinimum":0}},"required":["manifestSha","sizeBytes"],"additionalProperties":false},"arms":{"type":"array","items":{},"minItems":1,"maxItems":256},"maxSpendUsd":{"type":"number","exclusiveMinimum":0,"maximum":10000},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":86400},"idleTimeoutSeconds":{"type":"integer","exclusiveMinimum":0},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"count":{"type":"integer","exclusiveMinimum":0,"default":1},"vramGb":{"type":"number","exclusiveMinimum":0}},"required":["kind"],"additionalProperties":false}},"required":["maxSpendUsd","maxLifetimeSeconds","accelerator"],"additionalProperties":false},"GpuLeaseExecRequest":{"type":"object","properties":{"command":{"type":"string","minLength":1,"maxLength":8192},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":2147453647},"env":{"type":"object","additionalProperties":{"type":"string"}},"cwd":{"type":"string","minLength":1,"maxLength":2048}},"required":["command"],"additionalProperties":false},"GpuProviderStatus":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}},"keyUrl":{"type":"string"},"credential":{"type":"object","properties":{"verdict":{"type":"string","enum":["working","rejected","absent","indeterminate"]},"detail":{"type":"string"},"checkedAt":{"type":"string"},"latencyMs":{"type":"number"}},"required":["verdict","detail","checkedAt","latencyMs"]},"estimate":{"type":"object","properties":{"providerInstanceType":{"type":"string"},"region":{"type":"string"},"pricingSource":{"type":"string","enum":["live","configured"]},"providerCostPerHourUsd":{"type":"number"},"customerPricePerHourUsd":{"type":"number"}},"required":["providerCostPerHourUsd","customerPricePerHourUsd"]},"estimateError":{"type":"string"}},"required":["provider","state","envVars"]},"FleetDispatchStreamEvent":{"oneOf":[{"type":"object","properties":{"event":{"type":"string","enum":["fleet.dispatch.started"]},"data":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"type":{"type":"string","enum":["exec","prompt"]},"machineCount":{"type":"integer"}},"required":["fleetId","dispatchId","type","machineCount"]}},"required":["event","data"]},{"type":"object","properties":{"event":{"type":"string","enum":["fleet.dispatch.branch.started"]},"data":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"machineId":{"type":"string"}},"required":["fleetId","dispatchId","machineId"]}},"required":["event","data"]},{"type":"object","properties":{"event":{"type":"string","enum":["fleet.dispatch.branch.finished"]},"data":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"ok":{"type":"boolean"},"durationMs":{"type":"number"},"attempts":{"type":"integer","exclusiveMinimum":0},"result":{"type":"object","properties":{"exitCode":{"type":"number"},"stdout":{"type":"string"},"stderr":{"type":"string"}},"required":["exitCode","stdout","stderr"]},"prompt":{"type":"object","properties":{"success":{"type":"boolean"},"response":{"type":"string"},"error":{"type":"string"},"traceId":{"type":"string"},"usage":{"type":"object","properties":{"inputTokens":{"type":"number"},"outputTokens":{"type":"number"}},"required":["inputTokens","outputTokens"]},"metadata":{"type":"object","additionalProperties":{}}},"required":["success"]},"error":{"type":"object","properties":{"message":{"type":"string"},"status":{"type":"number"},"failureClass":{"type":"string","enum":["oom","timeout","dependency","infra","model","user_code"]},"retryAfterMs":{"type":"integer","minimum":0}},"required":["message"]}},"required":["machineId","sandboxId","ok","durationMs"]}},"required":["event","data"]},{"type":"object","properties":{"event":{"type":"string","enum":["fleet.dispatch.finished"]},"data":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"type":{"type":"string","enum":["exec","prompt"]},"durationMs":{"type":"number"},"results":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"ok":{"type":"boolean"},"durationMs":{"type":"number"},"attempts":{"type":"integer","exclusiveMinimum":0},"result":{"type":"object","properties":{"exitCode":{"type":"number"},"stdout":{"type":"string"},"stderr":{"type":"string"}},"required":["exitCode","stdout","stderr"]},"prompt":{"type":"object","properties":{"success":{"type":"boolean"},"response":{"type":"string"},"error":{"type":"string"},"traceId":{"type":"string"},"usage":{"type":"object","properties":{"inputTokens":{"type":"number"},"outputTokens":{"type":"number"}},"required":["inputTokens","outputTokens"]},"metadata":{"type":"object","additionalProperties":{}}},"required":["success"]},"error":{"type":"object","properties":{"message":{"type":"string"},"status":{"type":"number"},"failureClass":{"type":"string","enum":["oom","timeout","dependency","infra","model","user_code"]},"retryAfterMs":{"type":"integer","minimum":0}},"required":["message"]}},"required":["machineId","sandboxId","ok","durationMs"]}},"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.trace.v1"]},"traceId":{"type":"string"},"fleetId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"machineObservedLifecycleMs":{"type":"number","minimum":0},"dispatchFanoutMs":{"type":"number","minimum":0},"dispatchRuntimeMs":{"type":"number","minimum":0},"cleanupObservedMs":{"type":"number","minimum":0},"driverQueueMs":{"type":"number","minimum":0},"driverPlacementMs":{"type":"number","minimum":0},"driverProvisionMs":{"type":"number","minimum":0},"driverStartupMs":{"type":"number","minimum":0},"driverCleanupMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","machineObservedLifecycleMs","dispatchFanoutMs","dispatchRuntimeMs","cleanupObservedMs","driverQueueMs","driverPlacementMs","driverProvisionMs","driverStartupMs","driverCleanupMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0},"machineId":{"type":"string"}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["fleet.lifecycle.snapshot","fleet.machine.lifecycle.snapshot","fleet.workspace.lifecycle.snapshot","fleet.usage.snapshot","fleet.dispatch.result","fleet.insight.summary"]},"timestamp":{"type":"string"},"fleetId":{"type":"string"},"machineId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","fleetId","attributes"]}}},"required":["schemaVersion","traceId","fleetId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox_fleet"]},"fleetId":{"type":"string"}},"required":["type","fleetId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["fleetId","dispatchId","type","durationMs","results","trace","intelligence"]}},"required":["event","data"]},{"type":"object","properties":{"event":{"type":"string","enum":["fleet.dispatch.error"]},"data":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"message":{"type":"string"}},"required":["fleetId","dispatchId","message"]}},"required":["event","data"]}]},"SessionResponse":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"],"additionalProperties":{}},"CreateSessionRequest":{"type":"object","properties":{"projectId":{"type":"string"},"config":{"type":"object","properties":{"backend":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["api","cli"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"authMode":{"type":"string","enum":["api-key","oauth"]},"authFiles":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":4096},"content":{"type":"string","maxLength":1048576},"mode":{"type":"integer","minimum":0,"maximum":511}},"required":["path","content"],"additionalProperties":false},"maxItems":128},"apiKeyEnv":{"type":"string","maxLength":128,"pattern":"^[A-Z_][A-Z0-9_]*$"}},"additionalProperties":false},"server":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"hostname":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}}},"required":["mcp"],"additionalProperties":false},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","properties":{"containerType":{"type":"string","minLength":1,"maxLength":256},"profileSecretIsolation":{"type":"boolean","const":true},"isolationPrincipal":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"traceAttributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"additionalProperties":{"type":"string","maxLength":1024}},"telemetry":{"not":{}}},"additionalProperties":false}},"additionalProperties":false}},"additionalProperties":{}},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false}}},"SendMessageRequest":{"type":"object","properties":{"message":{"type":"string","minLength":1}},"required":["message"],"additionalProperties":false},"EgressUsage":{"type":"object","properties":{"orgId":{"type":"string"},"month":{"type":"string"},"plan":{"type":"string","enum":["free","pro","enterprise"]},"accountBytes":{"type":"integer","minimum":0,"description":"The account's egress this month, the figure the allowance is enforced against."},"includedBytes":{"type":"integer","minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free."},"overage":{"type":"string","enum":["billed","refused"],"description":"Beyond the allowance: billed at `usdPerGb` on paid plans; refused on Free, which is never charged."},"usdPerGb":{"type":"number","minimum":0,"description":"Price per decimal GB beyond the account's included egress on this plan."},"overageBytes":{"type":"integer","minimum":0,"description":"The account's bytes beyond its allowance; 0 on Free."},"projectedChargeUsd":{"type":"number","minimum":0,"description":"Sum of the sandboxes' `chargeUsd`: what the meter has charged or reserved for this month so far."},"unattributedBytes":{"type":"integer","minimum":0,"description":"Account egress recorded before per-sandbox month records existed, so it has no sandbox row."},"sandboxes":{"type":"array","items":{"$ref":"#/components/schemas/SandboxEgressMonthRow"}},"accountCapBytes":{"type":["integer","null"],"minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free.","deprecated":true},"sandboxIncludedBytes":{"type":["integer","null"],"minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free.","deprecated":true}},"required":["orgId","month","plan","accountBytes","includedBytes","overage","usdPerGb","overageBytes","projectedChargeUsd","unattributedBytes","sandboxes","accountCapBytes","sandboxIncludedBytes"]},"SandboxEgressMonthRow":{"type":"object","properties":{"sandboxId":{"type":"string"},"bytes":{"type":"integer","minimum":0},"chargeUsd":{"type":"number","minimum":0,"description":"USD of the egress charges the meter created for this sandbox and month, debited or awaiting replay."},"includedBytes":{"type":["integer","null"],"minimum":0,"description":"Egress included with the account each month, shared by its sandboxes: 2 TB on paid plans, 100 GiB on Free.","deprecated":true},"overageBytes":{"type":"integer","minimum":0,"deprecated":true}},"required":["sandboxId","bytes","chargeUsd","includedBytes","overageBytes"]},"UsageResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"orgId":{"type":"string"},"period":{"type":"string"},"sessionsCreated":{"type":"number"},"lifetimeSandboxesCreated":{"type":"number"},"messagesSent":{"type":"number"},"computeMs":{"type":"number"},"activeSessionCount":{"type":"number"},"active_sandboxes":{"type":"number"},"compute_minutes_today":{"type":"number"},"gpu_seconds_today":{"type":"number"},"gpu_cost_usd_today":{"type":"number"},"gpu_provider_cost_usd_today":{"type":"number"},"api_calls_today":{"type":"number"}},"required":["orgId","period","sessionsCreated","lifetimeSandboxesCreated","messagesSent","computeMs","activeSessionCount","active_sandboxes","compute_minutes_today","gpu_seconds_today","gpu_cost_usd_today","gpu_provider_cost_usd_today","api_calls_today"]}},"required":["success","data"]},"SubscriptionResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"orgId":{"type":"string"},"plan":{"type":"string","enum":["free","pro","enterprise"]},"status":{"type":"string","enum":["active","canceled","past_due"]},"currentPeriodEnd":{"type":"number"},"limits":{"type":"object","properties":{"monthlyBalanceUsd":{"type":"number","minimum":0},"maxConcurrent":{"type":"integer","minimum":0},"rateLimitPerMinute":{"type":"integer","minimum":0},"maxSnapshots":{"type":"integer","minimum":0},"overageAllowed":{"type":"boolean"},"creditFloorUsd":{"type":"number"},"egressIncludedBytes":{"type":"integer","minimum":0},"maxCpuCores":{"type":"number","minimum":0},"maxRamGB":{"type":"number","minimum":0},"maxStorageGB":{"type":"number","minimum":0},"inFlight":{"type":"object","properties":{"cpuCores":{"type":"number","minimum":0},"memoryMB":{"type":"integer","minimum":0},"diskGB":{"type":"integer","minimum":0}},"required":["cpuCores","memoryMB","diskGB"]},"poolQuotas":{"type":"object","additionalProperties":{"type":"integer","minimum":0}},"maxExecutionTtlMs":{"type":"integer","exclusiveMinimum":0},"allowAccelerators":{"type":"boolean"},"maxAccelerators":{"type":"integer","minimum":0}},"required":["monthlyBalanceUsd","maxConcurrent","rateLimitPerMinute","maxSnapshots","overageAllowed","creditFloorUsd","egressIncludedBytes","maxCpuCores","maxRamGB","maxStorageGB","allowAccelerators","maxAccelerators"]},"resourceCeiling":{"type":"object","properties":{"cpuMax":{"type":"number","minimum":0},"ramMaxGB":{"type":"number","minimum":0},"storageMaxGB":{"type":"number","minimum":0}},"required":["cpuMax","ramMaxGB","storageMaxGB"]},"balanceAvailableUsd":{"type":["number","null"]},"balanceLifetimeSpentUsd":{"type":"number"},"rates":{"type":"object","properties":{"cpuPerHr":{"type":"number"},"ramPerGbHr":{"type":"number"},"diskPerGbHr":{"type":"number"},"acceleratorPerDeviceHr":{"type":"number"},"intelligenceReportUsd":{"type":"number"},"minChargePerHr":{"type":"number"}},"required":["cpuPerHr","ramPerGbHr","diskPerGbHr","acceleratorPerDeviceHr","intelligenceReportUsd","minChargePerHr"]},"planTiers":{"type":"array","items":{"$ref":"#/components/schemas/PlanTierInfo"}}},"required":["orgId","plan","status","currentPeriodEnd","limits","resourceCeiling","planTiers"]}},"required":["success","data"]},"PlanTierInfo":{"type":"object","properties":{"id":{"type":"string","enum":["free","pro","enterprise"]},"label":{"type":"string"},"cpuMax":{"type":"number"},"ramMaxGB":{"type":"number"},"storageMaxGB":{"type":"number"},"acceleratorMax":{"type":"number"}},"required":["id","label","cpuMax","ramMaxGB","storageMaxGB","acceleratorMax"]},"SandboxPricingResponse":{"type":"object","properties":{"pools":{"type":"object","additionalProperties":{"type":"object","properties":{"cpuCoreHourUsd":{"type":"number","minimum":0},"memoryGBHourUsd":{"type":"number","minimum":0},"diskGBHourUsd":{"type":"number","minimum":0},"minChargeHourUsd":{"type":"number","minimum":0},"source":{"type":"string","enum":["reference-constants","pool-derived"]}},"required":["cpuCoreHourUsd","memoryGBHourUsd","diskGBHourUsd","minChargeHourUsd","source"]}}},"required":["pools"]},"SandboxEgressTermsResponse":{"type":"object","properties":{"period":{"type":"string","enum":["calendar-month"]},"metered":{"type":"string","enum":["outbound"]},"bytesPerGb":{"type":"number","enum":[1000000000]},"plans":{"type":"object","properties":{"free":{"type":"object","properties":{"scope":{"type":"string","enum":["account"]},"includedBytes":{"type":"integer","minimum":0},"overage":{"type":"string","enum":["billed","refused"]},"usdPerGb":{"type":"number","minimum":0}},"required":["scope","includedBytes","overage","usdPerGb"]},"pro":{"type":"object","properties":{"scope":{"type":"string","enum":["account"]},"includedBytes":{"type":"integer","minimum":0},"overage":{"type":"string","enum":["billed","refused"]},"usdPerGb":{"type":"number","minimum":0}},"required":["scope","includedBytes","overage","usdPerGb"]},"enterprise":{"type":"object","properties":{"scope":{"type":"string","enum":["account"]},"includedBytes":{"type":"integer","minimum":0},"overage":{"type":"string","enum":["billed","refused"]},"usdPerGb":{"type":"number","minimum":0}},"required":["scope","includedBytes","overage","usdPerGb"]}},"required":["free","pro","enterprise"]}},"required":["period","metered","bytesPerGb","plans"]},"RouterModelsResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{}}},"required":["object","data"]},"SandboxShapesResponse":{"type":"object","properties":{"presets":{"type":"array","items":{"$ref":"#/components/schemas/SandboxShapePreset"}},"default":{"type":"string","enum":["nano","small","medium","large"]},"ceiling":{"type":"object","properties":{"cpuCores":{"type":"number"},"memoryMB":{"type":"number"},"diskGB":{"type":"number"}},"required":["cpuCores","memoryMB","diskGB"]}},"required":["presets","default","ceiling"]},"SandboxShapePreset":{"type":"object","properties":{"name":{"type":"string","enum":["nano","small","medium","large"]},"cpuCores":{"type":"integer","exclusiveMinimum":0},"memoryMB":{"type":"integer","exclusiveMinimum":0},"diskGB":{"type":"integer","exclusiveMinimum":0}},"required":["name","cpuCores","memoryMB","diskGB"]},"Line":{"type":"object","properties":{"id":{"type":"string"},"transport":{"type":"string","enum":["imessage","sms","whatsapp","email","voice","telegram"]},"address":{"type":"string","description":"E.164 number, mailbox, or `@handle` on a shared router"},"connect":{"type":["string","null"],"description":"Text to send to `routerAddress` to reach this line"},"routerAddress":{"type":["string","null"]},"connectionId":{"type":"string"},"providerId":{"type":"string"},"providerNumberId":{"type":["string","null"]},"workspace":{"type":["object","null"],"properties":{"resourceType":{"type":"string","enum":["workspace"]},"resourceId":{"type":"string"}},"required":["resourceType","resourceId"]},"label":{"type":["string","null"]},"clientReference":{"type":["string","null"]},"status":{"type":"string","enum":["active","released"]},"voice":{"type":["object","null"],"properties":{"enabled":{"type":"boolean"},"state":{"type":"string","enum":["enabling","enabled","disabled"]},"ph0nyConnectionId":{"type":"string"},"ph0nyAgentId":{"type":"string"},"ph0nyPhoneNumberId":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["enabled","state","ph0nyConnectionId","ph0nyAgentId","ph0nyPhoneNumberId","updatedAt"]},"attachment":{"$ref":"#/components/schemas/LineAttachment"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","transport","address","connect","routerAddress","connectionId","providerId","providerNumberId","workspace","label","clientReference","status","voice","attachment","createdAt","updatedAt"]},"LineAttachment":{"type":["object","null"],"properties":{"id":{"type":"string"},"lineId":{"type":"string"},"runAsKeyId":{"type":"string","description":"Execution key ID; pass it back when reauthorizing"},"sandboxId":{"type":"string","description":"Empty for member instances and shared applications"},"instance":{"type":"object","properties":{"keyPrefix":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:@-]{0,99}$","description":"Each member's instance key is this prefix plus 32 hex digits"},"create":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":63},"environment":{"type":"string","minLength":1,"maxLength":128},"ownerContext":{"type":"string","enum":["isolated"]},"capabilities":{"type":"array","items":{"type":"string","enum":["computer_use"]},"maxItems":1},"resources":{"type":"object","properties":{"cpuCores":{"type":"number","exclusiveMinimum":0,"maximum":64},"memoryMB":{"type":"integer","exclusiveMinimum":0,"maximum":262144},"diskGB":{"type":"integer","exclusiveMinimum":0,"maximum":2048}},"additionalProperties":false},"egressPolicy":{"type":"object","additionalProperties":{},"description":"A sandbox egress policy"},"idleTimeoutSeconds":{"type":"integer","minimum":60,"maximum":7776000},"maxLifetimeSeconds":{"type":"integer","minimum":60,"maximum":7776000},"deleteAfterStoppedSeconds":{"type":"integer","minimum":60,"maximum":7776000},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":256},"description":"Up to 16 fields; keys up to 64 characters"}},"default":{},"additionalProperties":false}},"required":["keyPrefix"],"additionalProperties":false},"mode":{"type":"string","enum":["personal","shared"]},"payer":{"type":"string","enum":["owner","member"]},"unknownSenders":{"type":"string","enum":["reject","guest","onboard"]},"roles":{"type":"object","additionalProperties":{"type":"object","properties":{"context":{"type":"string","enum":["own","shared"],"description":"`shared` turns see other shared members' recent exchanges"},"tools":{"type":"string","enum":["act","chat"],"description":"`chat` sessions get no shell, files, subagents or MCP servers"}},"required":["context","tools"],"additionalProperties":false},"description":"Keyed by role name: a lowercase word of up to 32 characters"},"respond":{"type":"object","properties":{"kind":{"type":"string","enum":["agent","none"],"description":"`none` answers asks, coded replies and notices only, and runs no agent"},"profileId":{"type":"string","minLength":1,"maxLength":256,"description":"Stored agent profile; not with `backend.profile`"},"backend":{"type":"object","additionalProperties":{},"description":"Agent backend configuration for member sessions"},"rolePolicy":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"roles":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["general"]}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["scoped"]},"mcpServers":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z][A-Za-z0-9_-]{0,63}$"},"minItems":1,"maxItems":16}},"required":["kind","mcpServers"],"additionalProperties":false}]},"description":"Role name to its agent scope"},"members":{"type":"object","additionalProperties":{"type":"object","properties":{"backend":{"type":"object","additionalProperties":{},"description":"Agent backend configuration for member sessions"}},"required":["backend"],"additionalProperties":false},"description":"Member address to its own backend; up to 50"}},"required":["version","roles","members"],"additionalProperties":false},"telegram":{"type":"object","properties":{"mode":{"type":"string","enum":["moderation","mentions"]},"deleteClearSpam":{"type":"boolean","default":false},"turnsPerGroupPerHour":{"type":"integer","minimum":1,"maximum":1000,"default":120},"allowedChatIds":{"type":"array","items":{"type":"string","pattern":"^-[1-9]\\d{0,15}$"},"minItems":1,"maxItems":100}},"required":["mode"],"additionalProperties":false},"acknowledgement":{"type":"boolean"},"readReceipts":{"type":"boolean"},"application":{"type":"object","properties":{"url":{"type":"string"},"binding":{"type":"string"}},"required":["url","binding"]},"gate":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"pattern":"^https:\\/\\//i"},"context":{"type":"object","additionalProperties":{"type":"string","maxLength":256},"description":"Up to 8 fixed fields sent with every candidate"},"audience":{"type":"string","enum":["owner","members"],"default":"members"}},"required":["url"]}},"required":["kind","acknowledgement","readReceipts"],"additionalProperties":false},"limits":{"type":"object","properties":{"turnsPerMemberPerDay":{"type":"integer","minimum":1,"maximum":10000},"noticesPerSenderPerDay":{"type":"integer","minimum":0,"maximum":100},"noticesPerLinePerDay":{"type":"integer","minimum":0,"maximum":10000},"allowance":{"type":"object","properties":{"member":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"payer":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"role":{"type":"string","enum":["owner","member"]},"payerRole":{"type":"string","enum":["owner","member"],"description":"Needs `payer`"},"paywallNotice":{"type":"string","minLength":1,"maxLength":480},"meter":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"perMember":{"type":"boolean"}},"required":["meter"],"additionalProperties":false}},"required":["turnsPerMemberPerDay","noticesPerSenderPerDay","noticesPerLinePerDay"]},"status":{"type":"string","enum":["active","suspended","detached"]},"clientReference":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","lineId","runAsKeyId","sandboxId","mode","payer","unknownSenders","roles","respond","limits","status","clientReference","createdAt","updatedAt"]},"LinesForwarderError":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["LINES_API_KEY_REQUIRED","PLATFORM_NOT_CONFIGURED","PAYLOAD_TOO_LARGE","PLATFORM_UNAVAILABLE"]}},"required":["error","code"]},"LineMember":{"type":"object","properties":{"id":{"type":"string"},"lineId":{"type":"string"},"attachmentId":{"type":"string"},"address":{"type":"string"},"role":{"type":"string"},"label":{"type":["string","null"]},"subjectId":{"type":["string","null"]},"allowance":{"type":["object","null"],"properties":{"member":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"payer":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"role":{"type":"string","enum":["owner","member"]},"payerRole":{"type":"string","enum":["owner","member"],"description":"Needs `payer`"},"paywallNotice":{"type":"string","minLength":1,"maxLength":480}},"required":["member"],"additionalProperties":false},"source":{"type":"string","enum":["declared","guest","onboarded"]},"status":{"type":"string","enum":["invited","active","stopped","removed"],"description":"`invited` until the member's first message"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Member revision, as sent to an application callback"}},"required":["id","lineId","attachmentId","address","role","label","subjectId","allowance","source","status","createdAt","updatedAt"]},"LineSendReceipt":{"type":"object","properties":{"message":{"$ref":"#/components/schemas/LineMessage"},"delivery":{"type":["object","null"],"properties":{"provider":{"type":"string","enum":["meta_whatsapp"]},"messageId":{"type":"string"},"acceptedAt":{"type":"string"},"sentAt":{"type":["string","null"]},"deliveredAt":{"type":["string","null"]},"readAt":{"type":["string","null"]},"failedAt":{"type":["string","null"]},"failureCode":{"type":["number","null"]}},"required":["provider","messageId","acceptedAt","sentAt","deliveredAt","readAt","failedAt","failureCode"],"description":"Provider delivery facts; null when unproven"},"transport":{"type":["string","null"],"enum":["imessage","sms","whatsapp","email","voice","telegram",null]},"eligibleAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"reply":{"type":["object","null"],"properties":{"code":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"closed":{"type":"boolean"},"answer":{"type":["object","null"],"properties":{"messageId":{"type":"string"},"text":{"type":["string","null"]},"receivedAt":{"type":"string"},"option":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["messageId","text","receivedAt","option","note"]}},"required":["code","expiresAt","closed","answer"]}},"required":["message","delivery","transport","eligibleAt","expiresAt","reply"]},"LineMessage":{"type":"object","properties":{"id":{"type":"string"},"threadId":{"type":["string","null"]},"memberId":{"type":["string","null"]},"direction":{"type":"string","enum":["in","out"]},"kind":{"type":"string","enum":["agent","notice","send"]},"deliveryKey":{"type":"string"},"replyToId":{"type":["string","null"]},"turnId":{"type":["string","null"]},"text":{"type":["string","null"],"description":"Null after content retention ends"},"image":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]},"status":{"type":"string"},"errorCode":{"type":["string","null"]},"timeline":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"}]}},"createdAt":{"type":"string","format":"date-time"}},"required":["id","threadId","memberId","direction","kind","deliveryKey","replyToId","turnId","text","status","errorCode","timeline","createdAt"]},"LineThread":{"type":"object","properties":{"id":{"type":"string"},"lineId":{"type":"string"},"memberId":{"type":"string"},"sandboxId":{"type":"string"},"sessionId":{"type":"string"},"lastMessageAt":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["active","closed"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","lineId","memberId","sandboxId","sessionId","lastMessageAt","status","createdAt"]},"SandboxInstance":{"type":"object","properties":{"key":{"type":"string"},"generation":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Counts the key's sandboxes; each replacement adds one."},"sandboxId":{"type":["string","null"],"description":"The sandbox serving the key now, or null."},"profileVersion":{"type":["string","null"],"description":"The agent profile version that new sessions bind to."},"unstartableSince":{"type":["number","null"],"description":"Epoch milliseconds of the current sandbox's first failed start, or null."},"createdAt":{"type":"number","description":"Epoch milliseconds."},"updatedAt":{"type":"number","description":"Epoch milliseconds."}},"required":["key","generation","sandboxId","profileVersion","unstartableSince","createdAt","updatedAt"]},"Team":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"ownerCustomerId":{"type":"string"},"orgId":{"type":["string","null"]},"memberCount":{"type":"integer","minimum":0},"currentUserRole":{"type":"string","enum":["owner","admin","member","viewer"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"egressPolicy":{"type":["object","null"],"properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false}},"required":["id","name","ownerCustomerId","orgId","memberCount","currentUserRole","createdAt","updatedAt"]},"TeamMember":{"type":"object","properties":{"id":{"type":"string"},"teamId":{"type":"string"},"customerId":{"type":"string"},"customerEmail":{"type":"string"},"customerName":{"type":["string","null"]},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"status":{"type":"string","enum":["active","pending","removed"]},"joinedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","teamId","customerId","customerEmail","customerName","role","status","joinedAt","createdAt"]},"TeamInvitation":{"type":"object","properties":{"id":{"type":"string"},"teamId":{"type":"string"},"email":{"type":"string"},"invitedBy":{"type":"string"},"invitedByEmail":{"type":["string","null"]},"role":{"type":"string","enum":["admin","member","viewer"]},"token":{"type":"string"},"status":{"type":"string","enum":["pending","accepted","expired","revoked"]},"expiresAt":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","teamId","email","invitedBy","invitedByEmail","role","token","status","expiresAt","createdAt"]},"TeamSecret":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"createdBy":{"type":["string","null"]}},"required":["name","createdAt","createdBy"],"additionalProperties":false},"TeamTemplate":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"snapshotId":{"type":"string"},"sourceSandboxId":{"type":["string","null"]},"environment":{"type":"string"},"config":{"type":"object","additionalProperties":{}},"teamId":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","snapshotId","sourceSandboxId","environment","config","teamId","createdAt","updatedAt"]}},"parameters":{}},"paths":{"/auth/account":{"delete":{"summary":"Delete the account","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`. It also needs the `recent_auth` cookie that sign-in sets, which expires soon after. Marks the account deleted, revokes the API keys Sandbox created for it, except a team's keys, and clears the `session` cookie.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"responses":{"200":{"description":"Account deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]}},"required":["success","data"]}}}},"401":{"description":"No valid credential, or no `recent_auth` cookie from a recent sign-in","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RECENT_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"500":{"description":"The account could not be deleted","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/account`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/account`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/account\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/account\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/callback":{"get":{"summary":"Finish browser sign-in","description":"Takes the one-time `code` from Tangle sign-in and the `oauth_state` cookie set when sign-in started. Always answers with a redirect. On success it sets the `session` and `recent_auth` cookies and redirects to the `redirect_to` target from sign-in, by default `/dashboard`. When the account has two-factor sign-in on, it sets the `mfa_challenge` cookie and redirects to the dashboard `/mfa` page instead. A CLI sign-in redirects to its loopback `callback_url` with `grant_token`, `expires_at` and `session_expires_at`; exchange the `grant_token` at `POST /auth/cli/exchange`. On failure it redirects to `/login?error=<reason>` on the dashboard, or to the loopback `callback_url` with `error=<reason>`. Reasons include `missing_code`, `csrf_mismatch`, `auth_failed`, `email_verification_required` and `paid_access_required`.","security":[],"tags":["Sign-in"],"parameters":[{"schema":{"type":"string","description":"One-time code that Tangle sign-in returns."},"required":false,"description":"One-time code that Tangle sign-in returns.","name":"code","in":"query"},{"schema":{"type":"string","description":"State value from the sign-in redirect, returned unchanged."},"required":false,"description":"State value from the sign-in redirect, returned unchanged.","name":"state","in":"query"},{"schema":{"type":"string","description":"Error code from Tangle sign-in, when sign-in failed there."},"required":false,"description":"Error code from Tangle sign-in, when sign-in failed there.","name":"error","in":"query"},{"schema":{"type":"string","description":"Readable reason for `error`; the redirect uses it when present."},"required":false,"description":"Readable reason for `error`; the redirect uses it when present.","name":"error_description","in":"query"}],"responses":{"302":{"description":"Redirect to the dashboard, the two-factor page, the CLI loopback URL, or the login page with an error","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"},"Set-Cookie":{"schema":{"type":"string","description":"Sets the `session` and `recent_auth` cookies, or `mfa_challenge` when a second factor is needed."},"required":true,"description":"Sets the `session` and `recent_auth` cookies, or `mfa_challenge` when a second factor is needed."}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"The rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/callback`, baseUrl);\nconst code = process.env.SANDBOX_CODE;\nif (code !== undefined) url.searchParams.set(\"code\", code);\nconst state = process.env.SANDBOX_STATE;\nif (state !== undefined) url.searchParams.set(\"state\", state);\nconst error = process.env.SANDBOX_ERROR;\nif (error !== undefined) url.searchParams.set(\"error\", error);\nconst error_description = process.env.SANDBOX_ERROR_DESCRIPTION;\nif (error_description !== undefined) url.searchParams.set(\"error_description\", error_description);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/callback`, baseUrl);\nconst code = process.env.SANDBOX_CODE;\nif (code !== undefined) url.searchParams.set(\"code\", code);\nconst state = process.env.SANDBOX_STATE;\nif (state !== undefined) url.searchParams.set(\"state\", state);\nconst error = process.env.SANDBOX_ERROR;\nif (error !== undefined) url.searchParams.set(\"error\", error);\nconst error_description = process.env.SANDBOX_ERROR_DESCRIPTION;\nif (error_description !== undefined) url.searchParams.set(\"error_description\", error_description);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/callback\"\nquery = {}\n_code = os.environ.get(\"SANDBOX_CODE\")\nif _code is not None: query[\"code\"] = _code\n_state = os.environ.get(\"SANDBOX_STATE\")\nif _state is not None: query[\"state\"] = _state\n_error = os.environ.get(\"SANDBOX_ERROR\")\nif _error is not None: query[\"error\"] = _error\n_error_description = os.environ.get(\"SANDBOX_ERROR_DESCRIPTION\")\nif _error_description is not None: query[\"error_description\"] = _error_description\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/callback\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_CODE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}code=${SANDBOX_CODE}\"\nfi\nif [ -n \"${SANDBOX_STATE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}state=${SANDBOX_STATE}\"\nfi\nif [ -n \"${SANDBOX_ERROR:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}error=${SANDBOX_ERROR}\"\nfi\nif [ -n \"${SANDBOX_ERROR_DESCRIPTION:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}error_description=${SANDBOX_ERROR_DESCRIPTION}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/cli/callback":{"get":{"summary":"Forward a CLI sign-in return","description":"Takes no credential. Redirects to `GET /auth/callback` on the API origin with the same `code`, `state`, `error` and `error_description`.","security":[],"tags":["Sign-in"],"parameters":[{"schema":{"type":"string","description":"One-time code that Tangle sign-in returns."},"required":false,"description":"One-time code that Tangle sign-in returns.","name":"code","in":"query"},{"schema":{"type":"string","description":"State value from the sign-in redirect, returned unchanged."},"required":false,"description":"State value from the sign-in redirect, returned unchanged.","name":"state","in":"query"},{"schema":{"type":"string","description":"Error code from Tangle sign-in, when sign-in failed there."},"required":false,"description":"Error code from Tangle sign-in, when sign-in failed there.","name":"error","in":"query"},{"schema":{"type":"string","description":"Readable reason for `error`; the redirect uses it when present."},"required":false,"description":"Readable reason for `error`; the redirect uses it when present.","name":"error_description","in":"query"}],"responses":{"302":{"description":"Redirect to `GET /auth/callback`","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"The rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/cli/callback`, baseUrl);\nconst code = process.env.SANDBOX_CODE;\nif (code !== undefined) url.searchParams.set(\"code\", code);\nconst state = process.env.SANDBOX_STATE;\nif (state !== undefined) url.searchParams.set(\"state\", state);\nconst error = process.env.SANDBOX_ERROR;\nif (error !== undefined) url.searchParams.set(\"error\", error);\nconst error_description = process.env.SANDBOX_ERROR_DESCRIPTION;\nif (error_description !== undefined) url.searchParams.set(\"error_description\", error_description);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/callback`, baseUrl);\nconst code = process.env.SANDBOX_CODE;\nif (code !== undefined) url.searchParams.set(\"code\", code);\nconst state = process.env.SANDBOX_STATE;\nif (state !== undefined) url.searchParams.set(\"state\", state);\nconst error = process.env.SANDBOX_ERROR;\nif (error !== undefined) url.searchParams.set(\"error\", error);\nconst error_description = process.env.SANDBOX_ERROR_DESCRIPTION;\nif (error_description !== undefined) url.searchParams.set(\"error_description\", error_description);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/callback\"\nquery = {}\n_code = os.environ.get(\"SANDBOX_CODE\")\nif _code is not None: query[\"code\"] = _code\n_state = os.environ.get(\"SANDBOX_STATE\")\nif _state is not None: query[\"state\"] = _state\n_error = os.environ.get(\"SANDBOX_ERROR\")\nif _error is not None: query[\"error\"] = _error\n_error_description = os.environ.get(\"SANDBOX_ERROR_DESCRIPTION\")\nif _error_description is not None: query[\"error_description\"] = _error_description\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/callback\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_CODE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}code=${SANDBOX_CODE}\"\nfi\nif [ -n \"${SANDBOX_STATE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}state=${SANDBOX_STATE}\"\nfi\nif [ -n \"${SANDBOX_ERROR:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}error=${SANDBOX_ERROR}\"\nfi\nif [ -n \"${SANDBOX_ERROR_DESCRIPTION:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}error_description=${SANDBOX_ERROR_DESCRIPTION}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/cli/device":{"get":{"summary":"Show the device approval page","description":"Takes no credential; it reads the `session` cookie to see who is signed in. With a `user_code` and no session it redirects to `GET /auth/login`. When the dashboard runs on another origin it redirects to the dashboard's device page. Otherwise it answers an HTML page where the person approves the code.","security":[],"tags":["Sign-in"],"parameters":[{"schema":{"type":"string","description":"Eight letters and digits from the CLI, with or without the hyphen."},"required":false,"description":"Eight letters and digits from the CLI, with or without the hyphen.","name":"user_code","in":"query"},{"schema":{"type":"string","description":"`github`, `google` or `microsoft`. Any other value is ignored."},"required":false,"description":"`github`, `google` or `microsoft`. Any other value is ignored.","name":"provider","in":"query"}],"responses":{"200":{"description":"Approval page","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"Redirect to sign-in, or to the dashboard's device page","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"The rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/cli/device`, baseUrl);\nconst user_code = process.env.SANDBOX_USER_CODE;\nif (user_code !== undefined) url.searchParams.set(\"user_code\", user_code);\nconst provider = process.env.SANDBOX_PROVIDER;\nif (provider !== undefined) url.searchParams.set(\"provider\", provider);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device`, baseUrl);\nconst user_code = process.env.SANDBOX_USER_CODE;\nif (user_code !== undefined) url.searchParams.set(\"user_code\", user_code);\nconst provider = process.env.SANDBOX_PROVIDER;\nif (provider !== undefined) url.searchParams.set(\"provider\", provider);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/device\"\nquery = {}\n_user_code = os.environ.get(\"SANDBOX_USER_CODE\")\nif _user_code is not None: query[\"user_code\"] = _user_code\n_provider = os.environ.get(\"SANDBOX_PROVIDER\")\nif _provider is not None: query[\"provider\"] = _provider\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/device\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_USER_CODE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}user_code=${SANDBOX_USER_CODE}\"\nfi\nif [ -n \"${SANDBOX_PROVIDER:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}provider=${SANDBOX_PROVIDER}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/cli/device/authorize":{"post":{"summary":"Approve a device sign-in","description":"Takes the `session` cookie of the person approving the code. Without a session it redirects to `GET /auth/login`. Approving lets the CLI's next poll succeed. Answers an HTML page, or a 303 redirect to the dashboard's device page when `result_redirect` is `frontend` and the dashboard runs on another origin.","security":[],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"user_code":{"type":"string","description":"Eight letters and digits from the CLI, with or without the hyphen."},"provider":{"type":"string","description":"`github`, `google` or `microsoft`. Any other value is ignored."},"result_redirect":{"type":"string","description":"`frontend` to get a redirect instead of an HTML page."}},"required":["user_code"]}}}},"responses":{"200":{"description":"Code approved, or already approved","content":{"text/html":{"schema":{"type":"string"}}}},"302":{"description":"Redirect to sign-in because there is no session","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"}}},"303":{"description":"Redirect to the dashboard's device page with the result","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"}}},"400":{"description":"`user_code` is missing, invalid or expired","content":{"text/html":{"schema":{"type":"string"}}}},"401":{"description":"The session no longer passes key verification; sign in again","content":{"text/html":{"schema":{"type":"string"}}}},"403":{"description":"Key verification refused the session: no key binding, email not verified, paid access required, or the key belongs to another product","content":{"text/html":{"schema":{"type":"string"}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"text/html":{"schema":{"type":"string"}}}},"503":{"description":"Key verification or the rate limiter is unavailable; retry shortly","content":{"text/html":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/authorize`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/authorize`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/device/authorize\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/x-www-form-urlencoded\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/device/authorize\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/x-www-form-urlencoded\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/cli/device/poll":{"post":{"summary":"Poll a device sign-in","description":"Takes the `device_code` from `POST /auth/cli/device/start`. Answers 428 `AUTHORIZATION_PENDING` until the person approves the code. The approved answer is returned once; the device code is spent after it.","security":[],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string"}},"required":["device_code"]}}}},"responses":{"200":{"description":"Approved; the CLI credentials","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"api_key":{"type":"string","description":"Session token. Send it as a Bearer token."},"expires_at":{"type":"string","format":"date-time"}},"required":["customer_id","email","tier","api_key","expires_at"]}},"required":["success","data"]}}}},"400":{"description":"`device_code` is missing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["VALIDATION_ERROR"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"The device code is invalid or expired, or the approving session no longer passes key verification","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["EXPIRED_TOKEN"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the approving session: no key binding, email not verified, paid access required, or the key belongs to another product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"428":{"description":"The person has not approved the code","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["AUTHORIZATION_PENDING"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]},"data":{"type":"object","properties":{"interval":{"type":"integer","description":"Seconds between polls"},"expires_in":{"type":"integer","description":"Seconds"}},"required":["interval","expires_in"]}},"required":["success","error","data"]}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"500":{"description":"The approved grant lacks its credentials","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_GRANT"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification or the rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/poll`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/poll`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/device/poll\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/device/poll\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/cli/device/start":{"post":{"summary":"Start device sign-in","description":"Takes no credential. Show `user_code` to the person, open `verification_uri_complete` in a browser, and poll `POST /auth/cli/device/poll` with `device_code` every `interval` seconds until `expires_in` seconds have passed.","security":[],"tags":["Sign-in"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"`github`, `google` or `microsoft`. Any other value is ignored."}}}}}},"responses":{"200":{"description":"Device code issued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"device_code":{"type":"string","description":"Secret the CLI polls with. Do not show it."},"user_code":{"type":"string","description":"Code the person enters, in the form `ABCD-1234`."},"verification_uri":{"type":"string"},"verification_uri_complete":{"type":"string","description":"Verification page with `user_code` filled in."},"expires_in":{"type":"integer","description":"Seconds"},"interval":{"type":"integer","description":"Seconds between polls"}},"required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"]}},"required":["success","data"]}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"The rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/start`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/device/start`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/device/start\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/device/start\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/cli/exchange":{"post":{"summary":"Exchange a CLI grant for credentials","description":"Takes the `grant_token` that `GET /auth/callback` appended to the CLI's loopback `callback_url`. A grant token is short-lived.","security":[],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"grant_token":{"type":"string","description":"Token from the loopback redirect."}},"required":["grant_token"]}}}},"responses":{"200":{"description":"CLI credentials","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"api_key":{"type":"string","description":"Session token. Send it as a Bearer token."},"expires_at":{"type":"string","format":"date-time"}},"required":["customer_id","email","tier","api_key","expires_at"]}},"required":["success","data"]}}}},"400":{"description":"`grant_token` is missing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["VALIDATION_ERROR"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"The grant token is invalid or expired, or its session no longer passes key verification","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_GRANT"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the session: no key binding, email not verified, paid access required, or the key belongs to another product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification or the rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/exchange`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/exchange`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/exchange\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/exchange\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/cli/hub-key":{"post":{"summary":"Mint a Hub key for the CLI","description":"Takes the `session` cookie, a session token as a Bearer token, or an account API key as a Bearer token. Returns an API key for Hub; `expires_at` says when it stops working. Minting again for the same `device_id` revokes that device's previous key.","security":[{"BearerAuth":[]},{"SessionCookie":[]}],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"device_id":{"type":"string","description":"Stable id for this install: 1 to 64 letters, digits, dots, underscores or dashes."}},"required":["device_id"]}}}},"responses":{"200":{"description":"Hub key minted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"api_key":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"platform_url":{"type":"string"}},"required":["api_key","expires_at","platform_url"]}},"required":["success","data"]}}}},"400":{"description":"`device_id` is missing or malformed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_DEVICE_ID"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"No valid credential, or no customer for it","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UNAUTHORIZED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}},"402":{"description":"The key this request used has spent its budget and cannot fund a new key","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["KEY_BUDGET_EXHAUSTED"]},"nextStep":{"type":"string"},"details":{"type":"object","properties":{"keyId":{"type":"string"},"budgetUsd":{"type":"number"},"spentUsd":{"type":"number"},"reservedUsd":{"type":"number"}},"required":["keyId","budgetUsd","spentUsd","reservedUsd"]},"retryable":{"type":"boolean"}},"required":["message","code","details"]}},"required":["success","error"]}}}},"403":{"description":"Key verification refused the credential, or it carries no verified key","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_DELEGATION_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}},"502":{"description":"The key could not be minted, or key verification returned an incomplete identity","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["HUB_KEY_DELEGATION_FAILED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}},"503":{"description":"This deployment has no Tangle sign-in configured, or key verification is unavailable","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CredentialRefusal"},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["HUB_KEY_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/hub-key`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/hub-key`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/hub-key\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/hub-key\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/cli/login":{"get":{"summary":"Start CLI browser sign-in","description":"Takes no credential. Sets the `oauth_state` cookie and redirects the browser to Tangle sign-in. After sign-in the browser returns to `callback_url` with a `grant_token` for `POST /auth/cli/exchange`.","security":[],"tags":["Sign-in"],"parameters":[{"schema":{"type":"string","description":"Loopback URL the CLI listens on: `http://localhost`, `http://127.0.0.1` or `http://[::1]`, with any port and path."},"required":true,"description":"Loopback URL the CLI listens on: `http://localhost`, `http://127.0.0.1` or `http://[::1]`, with any port and path.","name":"callback_url","in":"query"}],"responses":{"302":{"description":"Redirect to Tangle sign-in","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"},"Set-Cookie":{"schema":{"type":"string","description":"Sets the `oauth_state` cookie."},"required":true,"description":"Sets the `oauth_state` cookie."}}},"400":{"description":"`callback_url` is missing or is not a loopback `http` URL","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_CALLBACK_URL"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"This deployment has no Tangle sign-in configured, or the rate limiter is unavailable","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_NOT_CONFIGURED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/cli/login`, baseUrl);\nconst callback_url = process.env.SANDBOX_CALLBACK_URL;\nif (!callback_url) throw new Error(\"Set SANDBOX_CALLBACK_URL\");\nif (callback_url !== undefined) url.searchParams.set(\"callback_url\", callback_url);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/cli/login`, baseUrl);\nconst callback_url = process.env.SANDBOX_CALLBACK_URL;\nif (!callback_url) throw new Error(\"Set SANDBOX_CALLBACK_URL\");\nif (callback_url !== undefined) url.searchParams.set(\"callback_url\", callback_url);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/cli/login\"\nquery = {}\n_callback_url = os.environ.get(\"SANDBOX_CALLBACK_URL\")\nif not _callback_url: raise SystemExit(\"Set SANDBOX_CALLBACK_URL\")\nif _callback_url is not None: query[\"callback_url\"] = _callback_url\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/cli/login\"\n# URL-encode each query value before exporting the parameter.\n: \"${SANDBOX_CALLBACK_URL:?Set SANDBOX_CALLBACK_URL}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}callback_url=${SANDBOX_CALLBACK_URL}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/login":{"get":{"summary":"Start browser sign-in","description":"Takes no credential. Sets the `oauth_state` cookie and redirects the browser to Tangle sign-in, which returns it to `GET /auth/callback`.","security":[],"tags":["Sign-in"],"parameters":[{"schema":{"type":"string","description":"`github`, `google` or `microsoft`. Any other value is ignored."},"required":false,"description":"`github`, `google` or `microsoft`. Any other value is ignored.","name":"provider","in":"query"},{"schema":{"type":"string","description":"Passed to Tangle sign-in unchanged."},"required":false,"description":"Passed to Tangle sign-in unchanged.","name":"mode","in":"query"},{"schema":{"type":"string","description":"Where to send the browser after sign-in. Accepted on the dashboard origin, or as the CLI device page on the API origin. Anything else becomes the dashboard URL."},"required":false,"description":"Where to send the browser after sign-in. Accepted on the dashboard origin, or as the CLI device page on the API origin. Anything else becomes the dashboard URL.","name":"redirect_to","in":"query"}],"responses":{"302":{"description":"Redirect to Tangle sign-in","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"},"Set-Cookie":{"schema":{"type":"string","description":"Sets the `oauth_state` cookie."},"required":true,"description":"Sets the `oauth_state` cookie."}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"503":{"description":"This deployment has no Tangle sign-in configured, or the rate limiter is unavailable","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_NOT_CONFIGURED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/login`, baseUrl);\nconst provider = process.env.SANDBOX_PROVIDER;\nif (provider !== undefined) url.searchParams.set(\"provider\", provider);\nconst mode = process.env.SANDBOX_MODE;\nif (mode !== undefined) url.searchParams.set(\"mode\", mode);\nconst redirect_to = process.env.SANDBOX_REDIRECT_TO;\nif (redirect_to !== undefined) url.searchParams.set(\"redirect_to\", redirect_to);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/login`, baseUrl);\nconst provider = process.env.SANDBOX_PROVIDER;\nif (provider !== undefined) url.searchParams.set(\"provider\", provider);\nconst mode = process.env.SANDBOX_MODE;\nif (mode !== undefined) url.searchParams.set(\"mode\", mode);\nconst redirect_to = process.env.SANDBOX_REDIRECT_TO;\nif (redirect_to !== undefined) url.searchParams.set(\"redirect_to\", redirect_to);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/login\"\nquery = {}\n_provider = os.environ.get(\"SANDBOX_PROVIDER\")\nif _provider is not None: query[\"provider\"] = _provider\n_mode = os.environ.get(\"SANDBOX_MODE\")\nif _mode is not None: query[\"mode\"] = _mode\n_redirect_to = os.environ.get(\"SANDBOX_REDIRECT_TO\")\nif _redirect_to is not None: query[\"redirect_to\"] = _redirect_to\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/login\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_PROVIDER:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}provider=${SANDBOX_PROVIDER}\"\nfi\nif [ -n \"${SANDBOX_MODE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}mode=${SANDBOX_MODE}\"\nfi\nif [ -n \"${SANDBOX_REDIRECT_TO:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}redirect_to=${SANDBOX_REDIRECT_TO}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"summary":"Start browser sign-in from a form","description":"Takes no credential. Accepts a JSON body or a form body. Sets the `oauth_state` cookie and redirects the browser to Tangle sign-in.","security":[],"tags":["Sign-in"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"Sign-in provider, passed to Tangle sign-in unchanged."},"email":{"type":"string","description":"Address to prefill. Tangle sign-in then asks for a fresh sign-in."},"redirect_to":{"type":"string","description":"Where to send the browser after sign-in. Accepted on the dashboard origin, or as the CLI device page on the API origin. Anything else becomes the dashboard URL."},"mode":{"type":"string","description":"Passed to Tangle sign-in unchanged."}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"Sign-in provider, passed to Tangle sign-in unchanged."},"email":{"type":"string","description":"Address to prefill. Tangle sign-in then asks for a fresh sign-in."},"redirect_to":{"type":"string","description":"Where to send the browser after sign-in. Accepted on the dashboard origin, or as the CLI device page on the API origin. Anything else becomes the dashboard URL."},"mode":{"type":"string","description":"Passed to Tangle sign-in unchanged."}}}}}},"responses":{"302":{"description":"Redirect to Tangle sign-in","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"},"Set-Cookie":{"schema":{"type":"string","description":"Sets the `oauth_state` cookie."},"required":true,"description":"Sets the `oauth_state` cookie."}}},"503":{"description":"This deployment has no Tangle sign-in configured","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_NOT_CONFIGURED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/login`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/login`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/login\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/login\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/logout":{"post":{"summary":"Sign out","description":"Takes no credential. Clears the `session` and `mfa_challenge` cookies. To also end the shared Tangle sign-in, send the browser to `signoutUrl`.","security":[],"tags":["Sign-in"],"responses":{"200":{"description":"Cookies cleared","headers":{"Set-Cookie":{"schema":{"type":"string","description":"Clears the `session` and `mfa_challenge` cookies."},"required":true,"description":"Clears the `session` and `mfa_challenge` cookies."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"loggedOut":{"type":"boolean","enum":[true]},"signoutUrl":{"type":["string","null"],"description":"Tangle sign-out URL, or null when no Tangle sign-in is configured."}},"required":["loggedOut","signoutUrl"]}},"required":["success","data"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/logout`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/logout`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/logout\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/logout\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"get":{"summary":"Sign out and redirect","description":"Takes no credential. Clears the `session` and `mfa_challenge` cookies, then redirects to the Tangle sign-out URL, or to the dashboard when no Tangle sign-in is configured.","security":[],"tags":["Sign-in"],"responses":{"302":{"description":"Redirect to Tangle sign-out, or to the dashboard","headers":{"Location":{"schema":{"type":"string","description":"Where the browser goes"},"required":true,"description":"Where the browser goes"},"Set-Cookie":{"schema":{"type":"string","description":"Clears the `session` and `mfa_challenge` cookies."},"required":true,"description":"Clears the `session` and `mfa_challenge` cookies."}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/logout`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/logout`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/logout\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/logout\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/mfa/backup-codes":{"post":{"summary":"Replace backup codes","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`. It also needs the `recent_auth` cookie that sign-in sets, which expires soon after. Needs a current code. The old backup codes stop working.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current code from the authenticator app, or an unused backup code."}},"required":["code"]}}}},"responses":{"200":{"description":"New backup codes","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"backup_codes":{"type":"array","items":{"type":"string"}}},"required":["backup_codes"]}},"required":["success","data"]}}}},"400":{"description":"`code` is wrong","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_CODE","INVALID_SECRET"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"No valid credential, or no `recent_auth` cookie from a recent sign-in","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RECENT_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"404":{"description":"Two-factor sign-in is not on","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NOT_ENROLLED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"500":{"description":"The two-factor change failed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INTERNAL_ERROR"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/backup-codes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/backup-codes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/backup-codes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/backup-codes\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/mfa/challenge":{"post":{"summary":"Complete a two-factor sign-in","description":"Takes the `mfa_challenge` cookie that sign-in sets when the account has two-factor sign-in on, and a current code or an unused backup code. On success it sets the `session` and `recent_auth` cookies and clears `mfa_challenge`. Each correct submission uses up the cookie. A wrong code sets a fresh `mfa_challenge` cookie with the same expiry; use it for the retry. For a CLI sign-in, `redirect_to` is the loopback `callback_url` carrying the `grant_token` for `POST /auth/cli/exchange`.","security":[{"MfaChallengeCookie":[]}],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current code from the authenticator app, or an unused backup code."}},"required":["code"]}}}},"responses":{"200":{"description":"Signed in","headers":{"Set-Cookie":{"schema":{"type":"string","description":"Sets the `session` and `recent_auth` cookies and clears `mfa_challenge`."},"required":true,"description":"Sets the `session` and `recent_auth` cookies and clears `mfa_challenge`."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"redirect_to":{"type":"string","description":"Where to send the browser next."},"session_expires_at":{"type":"string","format":"date-time"}},"required":["customer_id","email","redirect_to","session_expires_at"]}},"required":["success","data"]}}}},"400":{"description":"`code` is wrong","headers":{"Set-Cookie":{"schema":{"type":"string","description":"Sets a fresh `mfa_challenge` cookie."},"required":true,"description":"Sets a fresh `mfa_challenge` cookie."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_CODE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"No challenge cookie, an expired one, or the sign-in no longer passes key verification","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NO_CHALLENGE","INVALID_CHALLENGE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"The sign-in has no key binding, or key verification refused it","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_ACCESS_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"409":{"description":"Another challenge is in progress for this account, or this cookie was already used","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["CONCURRENT_CHALLENGE","CHALLENGE_ALREADY_USED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"429":{"description":"Too many requests from this address; wait `Retry-After` seconds","headers":{"Retry-After":{"schema":{"type":"string","description":"Seconds to wait"},"required":true,"description":"Seconds to wait"}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RATE_LIMITED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"},"retry_after_seconds":{"type":"integer"}},"required":["message","code","retry_after_seconds"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification or the rate limiter is unavailable; retry shortly","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["PLATFORM_UNAVAILABLE","RATE_LIMITER_UNAVAILABLE"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/challenge`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/challenge`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/challenge\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/challenge\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/mfa/disable":{"post":{"summary":"Turn off two-factor sign-in","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`. It also needs the `recent_auth` cookie that sign-in sets, which expires soon after. Needs a current code, so a stolen session cannot remove the second factor.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current code from the authenticator app, or an unused backup code."}},"required":["code"]}}}},"responses":{"200":{"description":"Two-factor sign-in is off","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"enabled":{"type":"boolean","enum":[false]}},"required":["enabled"]}},"required":["success","data"]}}}},"400":{"description":"`code` is wrong","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_CODE","INVALID_SECRET"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"No valid credential, or no `recent_auth` cookie from a recent sign-in","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RECENT_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"404":{"description":"Two-factor sign-in is not on","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NOT_ENROLLED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"500":{"description":"The two-factor change failed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INTERNAL_ERROR"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/disable`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/disable`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/disable\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/disable\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/mfa/enroll":{"post":{"summary":"Start two-factor enrollment","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`. It also needs the `recent_auth` cookie that sign-in sets, which expires soon after. Returns a new secret and backup codes. Two-factor sign-in stays off until `POST /auth/mfa/verify` accepts a code from the secret.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"responses":{"200":{"description":"Enrollment started","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"secret":{"type":"string","description":"Authenticator secret"},"otpauth_url":{"type":"string","description":"Authenticator URL to show as a QR code."},"backup_codes":{"type":"array","items":{"type":"string"},"description":"Single-use codes. Send them back to `/verify`."}},"required":["secret","otpauth_url","backup_codes"]}},"required":["success","data"]}}}},"401":{"description":"No valid credential, or no `recent_auth` cookie from a recent sign-in","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RECENT_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"500":{"description":"Enrollment could not start","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/mfa/enroll`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/enroll`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/enroll\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/enroll\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/mfa/status":{"get":{"summary":"Get two-factor status","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"responses":{"200":{"description":"Two-factor status","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"enrolledAt":{"type":"string","format":"date-time"},"pending":{"type":"boolean","description":"An enrollment was started and not verified."},"backupCodesRemaining":{"type":"integer"}},"required":["enabled","pending","backupCodesRemaining"]}},"required":["success","data"]}}}},"401":{"description":"No valid credential in the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"500":{"description":"The status could not be read","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/mfa/status`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/status`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/status\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/status\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/mfa/verify":{"post":{"summary":"Finish two-factor enrollment","description":"Takes the `session` cookie, or its token as a Bearer token, from a browser sign-in; an API key, CLI token or runtime token gets 403 `SESSION_AUTH_REQUIRED`. It also needs the `recent_auth` cookie that sign-in sets, which expires soon after. Turns on two-factor sign-in when `code` matches the secret from `POST /auth/mfa/enroll`.","security":[{"SessionCookie":[]}],"tags":["Sign-in"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Current code from the authenticator app."},"backup_codes":{"type":"array","items":{"type":"string"},"description":"The `backup_codes` that `/enroll` returned, unchanged."}},"required":["code","backup_codes"]}}}},"responses":{"200":{"description":"Two-factor sign-in is on","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"enabled":{"type":"boolean","enum":[true]}},"required":["enabled"]}},"required":["success","data"]}}}},"400":{"description":"`code` is wrong, or `backup_codes` is not the list `/enroll` returned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INVALID_CODE","INVALID_SECRET"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"401":{"description":"No valid credential, or no `recent_auth` cookie from a recent sign-in","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["RECENT_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Key verification refused the credential, or an API key, CLI token or runtime token called a route that needs a browser sign-in (`SESSION_AUTH_REQUIRED`)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["SESSION_AUTH_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"404":{"description":"No enrollment is pending, or it expired; start again with `/enroll`","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NOT_ENROLLED","NO_PENDING_ENROLLMENT","PENDING_EXPIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"500":{"description":"The two-factor change failed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["INTERNAL_ERROR"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/verify`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/mfa/verify`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/mfa/verify\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/mfa/verify\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/auth/providers":{"get":{"summary":"List sign-in methods","description":"Takes no credential. Each flag says whether this deployment offers the method.","security":[],"tags":["Sign-in"],"responses":{"200":{"description":"Sign-in methods","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"api_key":{"type":"boolean"},"session":{"type":"boolean"},"platform":{"type":"boolean"},"github":{"type":"boolean"},"google":{"type":"boolean"},"email_password":{"type":"boolean"},"dev_login":{"type":"boolean"}},"required":["api_key","session","platform","github","google","email_password","dev_login"]}},"required":["success","data"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/providers`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/providers`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/providers\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/providers\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/auth/session":{"get":{"summary":"Get the current session","description":"Takes the `session` cookie, a session token as a Bearer token, or an account API key as a Bearer token. Answers 200 with `authenticated: false` when no valid credential is sent.","security":[{"BearerAuth":[]},{"SessionCookie":[]}],"tags":["Sign-in"],"responses":{"200":{"description":"The signed-in account, or `authenticated: false`","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"authenticated":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"platform_user_id":{"type":["string","null"]},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"image":{"type":["string","null"],"description":"Avatar URL from the sign-in provider, or null."},"session_expires_at":{"type":"null"}},"required":["customer_id","platform_user_id","email","name","tier","image","session_expires_at"]}},"required":["success","authenticated","data"]},{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"authenticated":{"type":"boolean","enum":[false]},"data":{"type":"null"},"reason":{"type":"string","description":"Refusal slug, present when a credential was sent and refused. It separates a session that lost its entitlement from no session at all."},"invalidReason":{"type":"string","description":"The reason key verification gave for refusing the key."}},"required":["success","authenticated","data"]}]}}}},"403":{"description":"Key verification refused the credential: email not verified, paid access required, or the key belongs to another product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/session`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/session`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/session\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/session\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"summary":"Exchange an API key for a browser session","description":"Takes an account API key as a Bearer token; a session token or the `session` cookie gets 401. Sets the `session` and `recent_auth` cookies. When the account has two-factor sign-in on, sets the `mfa_challenge` cookie instead and answers 403 `MFA_REQUIRED`; finish with `POST /auth/mfa/challenge`.","security":[{"BearerAuth":[]},{"SessionCookie":[]}],"tags":["Sign-in"],"responses":{"200":{"description":"Session cookies set","headers":{"Set-Cookie":{"schema":{"type":"string","description":"Sets the `session` and `recent_auth` cookies."},"required":true,"description":"Sets the `session` and `recent_auth` cookies."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"session_expires_at":{"type":"string","format":"date-time"}},"required":["customer_id","email","tier","session_expires_at"]}},"required":["success","data"]}}}},"401":{"description":"No valid credential, or a session token in place of an API key","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UNAUTHORIZED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"Two-factor verification required, or key verification refused the credential","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["MFA_REQUIRED"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"mfa_required":{"type":"boolean","enum":[true]},"challenge_url":{"type":"string","description":"Dashboard page that collects the second factor."}},"required":["customer_id","email","mfa_required","challenge_url"]}},"required":["success","error","data"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"404":{"description":"No customer for this credential","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NOT_FOUND"]},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}},"retryable":{"type":"boolean"}},"required":["message","code"]}},"required":["success","error"]}}}},"502":{"description":"Key verification returned an incomplete identity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Key verification is unavailable; retry after the `Retry-After` seconds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/auth/session`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/auth/session`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/auth/session\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/auth/session\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes":{"post":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Create a sandbox","description":"E2B's `POST /sandboxes`. `templateID` is an id from `GET /e2b/templates`; `timeout` is the lifetime in seconds (default 15). Answers once the sandbox runs.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BNewSandbox"}}}},"responses":{"201":{"description":"Running sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BSandbox"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/e2b/sandboxes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/e2b/sandboxes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/e2b/sandboxes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"List sandboxes","description":"E2B's `GET /sandboxes`.","responses":{"200":{"description":"Sandboxes","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/E2BListedSandbox"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/e2b/sandboxes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/e2b/sandboxes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/e2b/sandboxes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes/{sandboxID}":{"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Get a sandbox","description":"E2B's `GET /sandboxes/{sandboxID}`.","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"}],"responses":{"200":{"description":"Sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BSandboxDetail"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Kill a sandbox","description":"E2B's `DELETE /sandboxes/{sandboxID}`. Answers after the sandbox is removed.","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"}],"responses":{"204":{"description":"Removed"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes/{sandboxID}/files":{"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Read a file","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string","description":"Absolute path in the sandbox."},"required":true,"description":"Absolute path in the sandbox.","name":"path","in":"query"}],"responses":{"200":{"description":"File content","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"text/plain":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/files\"\nquery = {}\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nif _path is not None: query[\"path\"] = _path\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/files\"\n# URL-encode each query value before exporting the parameter.\n: \"${SANDBOX_PATH:?Set SANDBOX_PATH}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}path=${SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Write a file","description":"Send multipart fields `path` and `file`, or the raw bytes with `?path=`. Files above 5242880 bytes answer 413.","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string","description":"Absolute path in the sandbox."},"required":false,"description":"Absolute path in the sandbox.","name":"path","in":"query"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"path":{"type":"string"},"file":{"type":"string","format":"binary"}},"required":["path","file"]}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"File written","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string"},"size":{"type":"number"}},"required":["path","size"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"multipart/form-data\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"multipart/form-data\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/files\"\nquery = {}\n_path = os.environ.get(\"SANDBOX_PATH\")\nif _path is not None: query[\"path\"] = _path\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"multipart/form-data\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/files\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_PATH:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}path=${SANDBOX_PATH}\"\nfi\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: multipart/form-data\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Delete a file","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string","description":"Absolute path in the sandbox."},"required":true,"description":"Absolute path in the sandbox.","name":"path","in":"query"}],"responses":{"204":{"description":"Deleted"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/files\"\nquery = {}\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nif _path is not None: query[\"path\"] = _path\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/files\"\n# URL-encode each query value before exporting the parameter.\n: \"${SANDBOX_PATH:?Set SANDBOX_PATH}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}path=${SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes/{sandboxID}/files/list":{"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"List a directory","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string","description":"Absolute path in the sandbox."},"required":true,"description":"Absolute path in the sandbox.","name":"path","in":"query"}],"responses":{"200":{"description":"Directory entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["file","dir"]},"path":{"type":"string"},"size":{"type":"number"}},"required":["name","type","path"]}}},"required":["entries"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files/list`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/files/list`, baseUrl);\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nif (path !== undefined) url.searchParams.set(\"path\", path);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/files/list\"\nquery = {}\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nif _path is not None: query[\"path\"] = _path\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/files/list\"\n# URL-encode each query value before exporting the parameter.\n: \"${SANDBOX_PATH:?Set SANDBOX_PATH}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}path=${SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes/{sandboxID}/processes":{"post":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Run a command","description":"Runs the command to completion and answers with its exit code and output. The E2B SDK's `commands.run` talks to the sandbox directly instead; this route is for plain HTTP callers.","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BProcessStart"}}}},"responses":{"200":{"description":"Finished process","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BProcess"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/processes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/processes\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/e2b/sandboxes/{sandboxID}/processes/{pid}":{"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Get a process","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string"},"required":true,"name":"pid","in":"path"}],"responses":{"200":{"description":"Process","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BProcess"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst pid = process.env.SANDBOX_PID;\nif (!pid) throw new Error(\"Set SANDBOX_PID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes/${encodeURIComponent(pid)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst pid = process.env.SANDBOX_PID;\nif (!pid) throw new Error(\"Set SANDBOX_PID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes/${encodeURIComponent(pid)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\n_pid = os.environ.get(\"SANDBOX_PID\")\nif not _pid: raise SystemExit(\"Set SANDBOX_PID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/processes/{quote(_pid or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/processes/${SANDBOX_PID:?Set SANDBOX_PID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Kill a process","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"},{"schema":{"type":"string"},"required":true,"name":"pid","in":"path"}],"responses":{"204":{"description":"Killed"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst pid = process.env.SANDBOX_PID;\nif (!pid) throw new Error(\"Set SANDBOX_PID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes/${encodeURIComponent(pid)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst pid = process.env.SANDBOX_PID;\nif (!pid) throw new Error(\"Set SANDBOX_PID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/processes/${encodeURIComponent(pid)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\n_pid = os.environ.get(\"SANDBOX_PID\")\nif not _pid: raise SystemExit(\"Set SANDBOX_PID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/processes/{quote(_pid or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/processes/${SANDBOX_PID:?Set SANDBOX_PID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/e2b/sandboxes/{sandboxID}/timeout":{"post":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"Set a sandbox timeout","description":"E2B's `POST /sandboxes/{sandboxID}/timeout`. Timeout updates are not supported: a valid request answers 501.","parameters":[{"schema":{"type":"string"},"required":true,"name":"sandboxID","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"timeout":{"type":"number","minimum":0,"description":"Seconds."}},"required":["timeout"]}}}},"responses":{"204":{"description":"Timeout set"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"403":{"description":"The key may not use this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"404":{"description":"Sandbox or path not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"501":{"description":"Timeout updates are not supported","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/timeout`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sandboxID = process.env.SANDBOX_SANDBOX_ID;\nif (!sandboxID) throw new Error(\"Set SANDBOX_SANDBOX_ID\");\nconst url = new URL(`/e2b/sandboxes/${encodeURIComponent(sandboxID)}/timeout`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sandboxID = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif not _sandboxID: raise SystemExit(\"Set SANDBOX_SANDBOX_ID\")\nurl = f\"{base_url}/e2b/sandboxes/{quote(_sandboxID or '', safe='')}/timeout\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/sandboxes/${SANDBOX_SANDBOX_ID:?Set SANDBOX_SANDBOX_ID}/timeout\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/e2b/templates":{"get":{"tags":["E2B compatibility"],"security":[{"E2BApiKey":[]},{"BearerAuth":[]}],"summary":"List templates","description":"One template per coding agent backend; pass its `templateID` to `POST /e2b/sandboxes`.","responses":{"200":{"description":"Templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/E2BTemplate"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}},"default":{"description":"Any other Sandbox API refusal, with its status and Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/E2BError"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/e2b/templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/e2b/templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/e2b/templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/e2b/templates\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/health":{"get":{"tags":["Service"],"security":[],"summary":"Check service health","description":"Needs no credential. status is ok while the service answers requests; a billing backlog never changes it and is reported under checks.","responses":{"200":{"description":"The service is answering","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"checks":{"type":"object","properties":{"chargeIntents":{"$ref":"#/components/schemas/ChargeIntentCheck"},"egressChargeIntents":{"$ref":"#/components/schemas/ChargeIntentCheck"},"gpuLeaseChargeIntents":{"$ref":"#/components/schemas/ChargeIntentCheck"}},"description":"One entry per billing queue. A queue whose reading is missing or stale is absent."}},"required":["status"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/health`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/health`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/health\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/health\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/subscription":{"get":{"tags":["Billing"],"summary":"Get the plan, credit and limits","description":"Reports the plan of the billing owner of the credential. Plan changes and top-ups are made at id.tangle.tools.","responses":{"200":{"description":"Plan, credit and limits","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro","enterprise"]},"status":{"type":"string","enum":["active","canceled","past_due"]},"creditsAvailableUsd":{"type":"number","description":"Credit balance in USD; the plan's monthly balance when the ledger returns none."},"creditsUsedUsd":{"type":"number","description":"Credit used in USD."},"monthlyBalanceUsd":{"type":"number","minimum":0},"maxConcurrentSandboxes":{"type":"integer","minimum":0,"description":"0 means the plan sets no cap and credits are the limit."},"overageAllowed":{"type":"boolean"},"limits":{"type":"object","properties":{"maxCpuCores":{"type":"number","minimum":0},"maxRamGB":{"type":"number","minimum":0},"maxStorageGB":{"type":"number","minimum":0}},"required":["maxCpuCores","maxRamGB","maxStorageGB"],"description":"Per-sandbox maximums."},"currentPeriodEnd":{"type":"number","description":"End of the billing period, in epoch milliseconds."}},"required":["plan","status","creditsAvailableUsd","creditsUsedUsd","monthlyBalanceUsd","maxConcurrentSandboxes","overageAllowed","limits","currentPeriodEnd"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"500":{"description":"The subscription could not be read","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["Failed to fetch subscription"]},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["error","code"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/subscription`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/subscription`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/subscription\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/subscription\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/usage":{"get":{"tags":["Usage"],"summary":"Get today's usage","description":"Counts for the billing owner of the credential, not for one sandbox.","responses":{"200":{"description":"Usage for the current period","content":{"application/json":{"schema":{"type":"object","properties":{"computeMinutes":{"type":"integer","description":"Compute minutes used in the period."},"activeSandboxes":{"type":"integer","description":"Sandboxes counted against the concurrent limit now."},"totalSandboxes":{"type":"integer","description":"Sandboxes ever created by the account; 0 when the count cannot be read."},"gpuSeconds":{"type":"number","description":"GPU lease seconds billed in the period."},"gpuCostUsd":{"type":"number","description":"GPU lease spend billed in the period, in USD."},"gpuProviderCostUsd":{"type":"number","description":"Provider cost of those GPU leases, in USD."},"periodStart":{"type":"string","format":"date-time"},"periodEnd":{"type":"string","format":"date-time"}},"required":["computeMinutes","activeSandboxes","totalSandboxes","gpuSeconds","gpuCostUsd","gpuProviderCostUsd","periodStart","periodEnd"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/usage`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/usage`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/usage\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/usage\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/account/me":{"get":{"tags":["Account"],"summary":"Get the current account (alias of /v1/me)","responses":{"200":{"description":"The caller's account","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"billing_owner_id":{"type":"string","description":"The Platform account that usage is billed to."},"api_key_id":{"type":["string","null"],"description":"The API key that authenticated the request; null when none did."},"billing_delegation_authorized":{"type":"boolean","description":"Whether this credential may choose a funded payer when it creates a sandbox."},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"created_at":{"type":"string","format":"date-time"}},"required":["customer_id","billing_owner_id","api_key_id","billing_delegation_authorized","email","tier","created_at"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"404":{"description":"The caller has no customer record (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/account/me`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/account/me`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/account/me\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/account/me\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/backends":{"get":{"tags":["Backends"],"summary":"List available agent backends","description":"Returns the per-backend capability registry (streaming, tool use, reasoning, multimodal, imageInput, MCP, sessions, interactions) and whether a backend runs only on the caller's own credential. Profiles are supplied inline on execution and provisioning requests.","responses":{"200":{"description":"Backends retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"backends":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"name":{"type":"string"},"description":{"type":"string"},"capabilities":{"type":"object","properties":{"streaming":{"type":"boolean"},"toolUse":{"type":"boolean"},"reasoning":{"type":"boolean"},"multimodal":{"type":"boolean"},"imageInput":{"type":"boolean"},"contextWindow":{"type":"number"},"mcp":{"type":"boolean"},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"boolean","enum":[true]}},"required":["mcp"]},"sessions":{"type":"boolean"},"configurable":{"type":"boolean"},"interactions":{"type":"array","items":{"type":"string","enum":["permission","question","plan"]}},"executionCorrelation":{"type":"boolean"}},"required":["streaming","toolUse","reasoning","multimodal","imageInput","contextWindow","mcp","sessions","configurable","interactions","executionCorrelation"]},"callerCredentialOnly":{"type":"boolean"},"unavailable":{"type":["object","null"],"properties":{"capability":{"type":"string","enum":["docker"]},"reason":{"type":"string"}},"required":["capability","reason"]}},"required":["type","name","description","capabilities","callerCredentialOnly","unavailable"]}},"timestamp":{"type":"string","format":"date-time"}},"required":["backends","timestamp"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/backends`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/backends`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/backends\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/backends\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/billing/balance":{"get":{"tags":["Billing"],"summary":"Get the credit balance","description":"Read from id.tangle.tools for the billing owner of the credential; the query string is passed through.","parameters":[{"schema":{"type":"string","description":"Read a team wallet. The caller must be a member."},"required":false,"description":"Read a team wallet. The caller must be a member.","name":"teamId","in":"query"}],"responses":{"200":{"description":"The wallet balance","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"balance":{"type":"number","description":"Balance in USD."},"fundedBalance":{"type":"number","description":"Paid, admin-granted and transferred credit in USD. Negative when post-paid usage exceeds it."},"lifetimeSpent":{"type":"number","description":"Total spent in USD."},"requiresInitialFunding":{"type":"boolean","description":"True for a wallet with no balance, spend or funding history. For display only."},"updatedAt":{"type":"string"},"activeHoldUsd":{"type":"number","description":"Wallet cash fenced by active authorizations, in USD."},"scopedCredits":{"type":"array","items":{"type":"object","properties":{"scope":{"type":"string"},"remainingUsd":{"type":"number","description":"Credit a charge in this scope may draw now."},"heldUsd":{"type":"number","description":"Credit under a hold that authorizes no new compute."},"nextExpiresAt":{"type":"string"}},"required":["scope","remainingUsd","heldUsd"]},"description":"Credit that only one kind of spend may redeem."},"wallet":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["personal","team"]}},"required":["id","kind"]},"walletPolicy":{"type":"object","properties":{"effectivePlan":{"type":"string"},"overageEligible":{"type":"boolean"},"overageEligibilityReason":{"type":"string"},"creditFloorUsd":{"type":"number","description":"Balance floor in USD; 0 unless overage is allowed."}},"required":["effectivePlan","overageEligible","overageEligibilityReason","creditFloorUsd"]}},"required":["balance","fundedBalance","lifetimeSpent","requiresInitialFunding","updatedAt","activeHoldUsd","scopedCredits","wallet","walletPolicy"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The caller is not a member of the team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"id.tangle.tools is unreachable (PLATFORM_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"504":{"description":"id.tangle.tools did not answer in time (PLATFORM_TIMEOUT)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/billing/balance`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/billing/balance`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/billing/balance\"\nquery = {}\n_teamId = os.environ.get(\"SANDBOX_TEAM_ID\")\nif _teamId is not None: query[\"teamId\"] = _teamId\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/billing/balance\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_TEAM_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}teamId=${SANDBOX_TEAM_ID}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/billing/transactions":{"get":{"tags":["Billing"],"summary":"List credit transactions","description":"Read from id.tangle.tools for the billing owner of the credential; the query string is passed through.","parameters":[{"schema":{"type":"string","description":"Read a team wallet. The caller must be a member."},"required":false,"description":"Read a team wallet. The caller must be a member.","name":"teamId","in":"query"},{"schema":{"type":"string","description":"Rows per page."},"required":false,"description":"Rows per page.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Rows to skip."},"required":false,"description":"Rows to skip.","name":"offset","in":"query"},{"schema":{"type":"string","description":"createdAt (default), amount or type."},"required":false,"description":"createdAt (default), amount or type.","name":"sort","in":"query"},{"schema":{"type":"string","description":"desc (default) or asc."},"required":false,"description":"desc (default) or asc.","name":"sortDirection","in":"query"},{"schema":{"type":"string","description":"resource returns statement lines instead of ledger rows."},"required":false,"description":"resource returns statement lines instead of ledger rows.","name":"groupBy","in":"query"},{"schema":{"type":"string","description":"Transaction type."},"required":false,"description":"Transaction type.","name":"type","in":"query"},{"schema":{"type":"string","description":"Product; an empty value matches rows with none."},"required":false,"description":"Product; an empty value matches rows with none.","name":"product","in":"query"},{"schema":{"type":"string","description":"API key that made the charge."},"required":false,"description":"API key that made the charge.","name":"keyId","in":"query"},{"schema":{"type":"string","description":"App the charge came from."},"required":false,"description":"App the charge came from.","name":"originAppId","in":"query"},{"schema":{"type":"string","description":"Lineage of the charging credential."},"required":false,"description":"Lineage of the charging credential.","name":"lineageId","in":"query"},{"schema":{"type":"string","description":"Actor that minted the charging key."},"required":false,"description":"Actor that minted the charging key.","name":"mintActorId","in":"query"},{"schema":{"type":"string","description":"User the charge is attributed to."},"required":false,"description":"User the charge is attributed to.","name":"attributionActorUserId","in":"query"},{"schema":{"type":"string","description":"Type of the charged resource."},"required":false,"description":"Type of the charged resource.","name":"resourceType","in":"query"},{"schema":{"type":"string","description":"Id of the charged resource."},"required":false,"description":"Id of the charged resource.","name":"resourceId","in":"query"},{"schema":{"type":"string","description":"Statement line key, for example sandbox:<id>."},"required":false,"description":"Statement line key, for example sandbox:<id>.","name":"groupKey","in":"query"},{"schema":{"type":"string","description":"Model that was charged."},"required":false,"description":"Model that was charged.","name":"modelId","in":"query"},{"schema":{"type":"string","description":"Earliest row, as an ISO date or time."},"required":false,"description":"Earliest row, as an ISO date or time.","name":"dateFrom","in":"query"},{"schema":{"type":"string","description":"Latest row, as an ISO date or time."},"required":false,"description":"Latest row, as an ISO date or time.","name":"dateTo","in":"query"},{"schema":{"type":"string","description":"Text to match in the description."},"required":false,"description":"Text to match in the description.","name":"search","in":"query"}],"responses":{"200":{"description":"A page of the wallet's transactions","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"ownerType":{"type":"string"},"amount":{"type":"number","description":"Signed amount in USD: negative for a charge."},"type":{"type":"string"},"provenance":{"type":"string"},"product":{"type":["string","null"]},"description":{"type":["string","null"]},"createdAt":{"type":"string"},"memberUserId":{"type":["string","null"]},"keyId":{"type":["string","null"]},"referenceId":{"type":["string","null"]},"traceId":{"type":["string","null"]},"modelId":{"type":["string","null"]},"inputTokens":{"type":["number","null"]},"outputTokens":{"type":["number","null"]}},"required":["id","userId","ownerType","amount","type","provenance","product","description","createdAt"]},{"type":"object","properties":{"id":{"type":"string"},"day":{"type":"string","description":"UTC day of the line."},"type":{"type":"string"},"product":{"type":["string","null"]},"groupKey":{"type":["string","null"]},"modelId":{"type":["string","null"]},"description":{"type":["string","null"]},"amount":{"type":"number","description":"Signed sum of the line's charges in USD."},"count":{"type":"integer","description":"Ledger rows in the line."},"firstAt":{"type":"string"},"lastAt":{"type":"string"},"drill":{"type":["object","null"],"properties":{"groupKey":{"type":"string"},"modelId":{"type":"string"},"type":{"type":"string"},"product":{"type":"string"}},"required":["type"],"description":"Filters that return the ledger rows the line sums."}},"required":["id","day","type","product","groupKey","modelId","description","amount","count","firstAt","lastAt","drill"]}]},"description":"Ledger rows, or with groupBy=resource one statement line per resource per UTC day."},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"grouped":{"type":"boolean","enum":[true]},"wallet":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["personal","team"]}},"required":["id","kind"]}},"required":["items","total","limit","offset","wallet"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The caller is not a member of the team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"id.tangle.tools is unreachable (PLATFORM_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"504":{"description":"id.tangle.tools did not answer in time (PLATFORM_TIMEOUT)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/billing/transactions`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst sort = process.env.SANDBOX_SORT;\nif (sort !== undefined) url.searchParams.set(\"sort\", sort);\nconst sortDirection = process.env.SANDBOX_SORT_DIRECTION;\nif (sortDirection !== undefined) url.searchParams.set(\"sortDirection\", sortDirection);\nconst groupBy = process.env.SANDBOX_GROUP_BY;\nif (groupBy !== undefined) url.searchParams.set(\"groupBy\", groupBy);\nconst type = process.env.SANDBOX_TYPE;\nif (type !== undefined) url.searchParams.set(\"type\", type);\nconst product = process.env.SANDBOX_PRODUCT;\nif (product !== undefined) url.searchParams.set(\"product\", product);\nconst keyId = process.env.SANDBOX_KEY_ID;\nif (keyId !== undefined) url.searchParams.set(\"keyId\", keyId);\nconst originAppId = process.env.SANDBOX_ORIGIN_APP_ID;\nif (originAppId !== undefined) url.searchParams.set(\"originAppId\", originAppId);\nconst lineageId = process.env.SANDBOX_LINEAGE_ID;\nif (lineageId !== undefined) url.searchParams.set(\"lineageId\", lineageId);\nconst mintActorId = process.env.SANDBOX_MINT_ACTOR_ID;\nif (mintActorId !== undefined) url.searchParams.set(\"mintActorId\", mintActorId);\nconst attributionActorUserId = process.env.SANDBOX_ATTRIBUTION_ACTOR_USER_ID;\nif (attributionActorUserId !== undefined) url.searchParams.set(\"attributionActorUserId\", attributionActorUserId);\nconst resourceType = process.env.SANDBOX_RESOURCE_TYPE;\nif (resourceType !== undefined) url.searchParams.set(\"resourceType\", resourceType);\nconst resourceId = process.env.SANDBOX_RESOURCE_ID;\nif (resourceId !== undefined) url.searchParams.set(\"resourceId\", resourceId);\nconst groupKey = process.env.SANDBOX_GROUP_KEY;\nif (groupKey !== undefined) url.searchParams.set(\"groupKey\", groupKey);\nconst modelId = process.env.SANDBOX_MODEL_ID;\nif (modelId !== undefined) url.searchParams.set(\"modelId\", modelId);\nconst dateFrom = process.env.SANDBOX_DATE_FROM;\nif (dateFrom !== undefined) url.searchParams.set(\"dateFrom\", dateFrom);\nconst dateTo = process.env.SANDBOX_DATE_TO;\nif (dateTo !== undefined) url.searchParams.set(\"dateTo\", dateTo);\nconst search = process.env.SANDBOX_SEARCH;\nif (search !== undefined) url.searchParams.set(\"search\", search);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/billing/transactions`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst sort = process.env.SANDBOX_SORT;\nif (sort !== undefined) url.searchParams.set(\"sort\", sort);\nconst sortDirection = process.env.SANDBOX_SORT_DIRECTION;\nif (sortDirection !== undefined) url.searchParams.set(\"sortDirection\", sortDirection);\nconst groupBy = process.env.SANDBOX_GROUP_BY;\nif (groupBy !== undefined) url.searchParams.set(\"groupBy\", groupBy);\nconst type = process.env.SANDBOX_TYPE;\nif (type !== undefined) url.searchParams.set(\"type\", type);\nconst product = process.env.SANDBOX_PRODUCT;\nif (product !== undefined) url.searchParams.set(\"product\", product);\nconst keyId = process.env.SANDBOX_KEY_ID;\nif (keyId !== undefined) url.searchParams.set(\"keyId\", keyId);\nconst originAppId = process.env.SANDBOX_ORIGIN_APP_ID;\nif (originAppId !== undefined) url.searchParams.set(\"originAppId\", originAppId);\nconst lineageId = process.env.SANDBOX_LINEAGE_ID;\nif (lineageId !== undefined) url.searchParams.set(\"lineageId\", lineageId);\nconst mintActorId = process.env.SANDBOX_MINT_ACTOR_ID;\nif (mintActorId !== undefined) url.searchParams.set(\"mintActorId\", mintActorId);\nconst attributionActorUserId = process.env.SANDBOX_ATTRIBUTION_ACTOR_USER_ID;\nif (attributionActorUserId !== undefined) url.searchParams.set(\"attributionActorUserId\", attributionActorUserId);\nconst resourceType = process.env.SANDBOX_RESOURCE_TYPE;\nif (resourceType !== undefined) url.searchParams.set(\"resourceType\", resourceType);\nconst resourceId = process.env.SANDBOX_RESOURCE_ID;\nif (resourceId !== undefined) url.searchParams.set(\"resourceId\", resourceId);\nconst groupKey = process.env.SANDBOX_GROUP_KEY;\nif (groupKey !== undefined) url.searchParams.set(\"groupKey\", groupKey);\nconst modelId = process.env.SANDBOX_MODEL_ID;\nif (modelId !== undefined) url.searchParams.set(\"modelId\", modelId);\nconst dateFrom = process.env.SANDBOX_DATE_FROM;\nif (dateFrom !== undefined) url.searchParams.set(\"dateFrom\", dateFrom);\nconst dateTo = process.env.SANDBOX_DATE_TO;\nif (dateTo !== undefined) url.searchParams.set(\"dateTo\", dateTo);\nconst search = process.env.SANDBOX_SEARCH;\nif (search !== undefined) url.searchParams.set(\"search\", search);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/billing/transactions\"\nquery = {}\n_teamId = os.environ.get(\"SANDBOX_TEAM_ID\")\nif _teamId is not None: query[\"teamId\"] = _teamId\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_offset = os.environ.get(\"SANDBOX_OFFSET\")\nif _offset is not None: query[\"offset\"] = _offset\n_sort = os.environ.get(\"SANDBOX_SORT\")\nif _sort is not None: query[\"sort\"] = _sort\n_sortDirection = os.environ.get(\"SANDBOX_SORT_DIRECTION\")\nif _sortDirection is not None: query[\"sortDirection\"] = _sortDirection\n_groupBy = os.environ.get(\"SANDBOX_GROUP_BY\")\nif _groupBy is not None: query[\"groupBy\"] = _groupBy\n_type = os.environ.get(\"SANDBOX_TYPE\")\nif _type is not None: query[\"type\"] = _type\n_product = os.environ.get(\"SANDBOX_PRODUCT\")\nif _product is not None: query[\"product\"] = _product\n_keyId = os.environ.get(\"SANDBOX_KEY_ID\")\nif _keyId is not None: query[\"keyId\"] = _keyId\n_originAppId = os.environ.get(\"SANDBOX_ORIGIN_APP_ID\")\nif _originAppId is not None: query[\"originAppId\"] = _originAppId\n_lineageId = os.environ.get(\"SANDBOX_LINEAGE_ID\")\nif _lineageId is not None: query[\"lineageId\"] = _lineageId\n_mintActorId = os.environ.get(\"SANDBOX_MINT_ACTOR_ID\")\nif _mintActorId is not None: query[\"mintActorId\"] = _mintActorId\n_attributionActorUserId = os.environ.get(\"SANDBOX_ATTRIBUTION_ACTOR_USER_ID\")\nif _attributionActorUserId is not None: query[\"attributionActorUserId\"] = _attributionActorUserId\n_resourceType = os.environ.get(\"SANDBOX_RESOURCE_TYPE\")\nif _resourceType is not None: query[\"resourceType\"] = _resourceType\n_resourceId = os.environ.get(\"SANDBOX_RESOURCE_ID\")\nif _resourceId is not None: query[\"resourceId\"] = _resourceId\n_groupKey = os.environ.get(\"SANDBOX_GROUP_KEY\")\nif _groupKey is not None: query[\"groupKey\"] = _groupKey\n_modelId = os.environ.get(\"SANDBOX_MODEL_ID\")\nif _modelId is not None: query[\"modelId\"] = _modelId\n_dateFrom = os.environ.get(\"SANDBOX_DATE_FROM\")\nif _dateFrom is not None: query[\"dateFrom\"] = _dateFrom\n_dateTo = os.environ.get(\"SANDBOX_DATE_TO\")\nif _dateTo is not None: query[\"dateTo\"] = _dateTo\n_search = os.environ.get(\"SANDBOX_SEARCH\")\nif _search is not None: query[\"search\"] = _search\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/billing/transactions\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_TEAM_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}teamId=${SANDBOX_TEAM_ID}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_OFFSET:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}offset=${SANDBOX_OFFSET}\"\nfi\nif [ -n \"${SANDBOX_SORT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}sort=${SANDBOX_SORT}\"\nfi\nif [ -n \"${SANDBOX_SORT_DIRECTION:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}sortDirection=${SANDBOX_SORT_DIRECTION}\"\nfi\nif [ -n \"${SANDBOX_GROUP_BY:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}groupBy=${SANDBOX_GROUP_BY}\"\nfi\nif [ -n \"${SANDBOX_TYPE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}type=${SANDBOX_TYPE}\"\nfi\nif [ -n \"${SANDBOX_PRODUCT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}product=${SANDBOX_PRODUCT}\"\nfi\nif [ -n \"${SANDBOX_KEY_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}keyId=${SANDBOX_KEY_ID}\"\nfi\nif [ -n \"${SANDBOX_ORIGIN_APP_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}originAppId=${SANDBOX_ORIGIN_APP_ID}\"\nfi\nif [ -n \"${SANDBOX_LINEAGE_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}lineageId=${SANDBOX_LINEAGE_ID}\"\nfi\nif [ -n \"${SANDBOX_MINT_ACTOR_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}mintActorId=${SANDBOX_MINT_ACTOR_ID}\"\nfi\nif [ -n \"${SANDBOX_ATTRIBUTION_ACTOR_USER_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}attributionActorUserId=${SANDBOX_ATTRIBUTION_ACTOR_USER_ID}\"\nfi\nif [ -n \"${SANDBOX_RESOURCE_TYPE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}resourceType=${SANDBOX_RESOURCE_TYPE}\"\nfi\nif [ -n \"${SANDBOX_RESOURCE_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}resourceId=${SANDBOX_RESOURCE_ID}\"\nfi\nif [ -n \"${SANDBOX_GROUP_KEY:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}groupKey=${SANDBOX_GROUP_KEY}\"\nfi\nif [ -n \"${SANDBOX_MODEL_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}modelId=${SANDBOX_MODEL_ID}\"\nfi\nif [ -n \"${SANDBOX_DATE_FROM:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}dateFrom=${SANDBOX_DATE_FROM}\"\nfi\nif [ -n \"${SANDBOX_DATE_TO:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}dateTo=${SANDBOX_DATE_TO}\"\nfi\nif [ -n \"${SANDBOX_SEARCH:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}search=${SANDBOX_SEARCH}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/billing/usage":{"get":{"tags":["Billing"],"summary":"Get spend by product","description":"Read from id.tangle.tools for the billing owner of the credential; the query string is passed through. Only debits count.","parameters":[{"schema":{"type":"string","description":"Read a team wallet. The caller must be a member."},"required":false,"description":"Read a team wallet. The caller must be a member.","name":"teamId","in":"query"},{"schema":{"type":"string","description":"Start of the range, as an ISO date or time."},"required":false,"description":"Start of the range, as an ISO date or time.","name":"from","in":"query"},{"schema":{"type":"string","description":"End of the range, as an ISO date or time."},"required":false,"description":"End of the range, as an ISO date or time.","name":"to","in":"query"}],"responses":{"200":{"description":"Spend per product","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"type":"object","properties":{"product":{"type":["string","null"]},"totalSpent":{"type":"number","description":"Spend in USD, net of payment returns."},"count":{"type":"integer"}},"required":["product","totalSpent","count"]}}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The caller is not a member of the team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"id.tangle.tools is unreachable (PLATFORM_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"504":{"description":"id.tangle.tools did not answer in time (PLATFORM_TIMEOUT)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/billing/usage`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst from = process.env.SANDBOX_FROM;\nif (from !== undefined) url.searchParams.set(\"from\", from);\nconst to = process.env.SANDBOX_TO;\nif (to !== undefined) url.searchParams.set(\"to\", to);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/billing/usage`, baseUrl);\nconst teamId = process.env.SANDBOX_TEAM_ID;\nif (teamId !== undefined) url.searchParams.set(\"teamId\", teamId);\nconst from = process.env.SANDBOX_FROM;\nif (from !== undefined) url.searchParams.set(\"from\", from);\nconst to = process.env.SANDBOX_TO;\nif (to !== undefined) url.searchParams.set(\"to\", to);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/billing/usage\"\nquery = {}\n_teamId = os.environ.get(\"SANDBOX_TEAM_ID\")\nif _teamId is not None: query[\"teamId\"] = _teamId\n_from = os.environ.get(\"SANDBOX_FROM\")\nif _from is not None: query[\"from\"] = _from\n_to = os.environ.get(\"SANDBOX_TO\")\nif _to is not None: query[\"to\"] = _to\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/billing/usage\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_TEAM_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}teamId=${SANDBOX_TEAM_ID}\"\nfi\nif [ -n \"${SANDBOX_FROM:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}from=${SANDBOX_FROM}\"\nfi\nif [ -n \"${SANDBOX_TO:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}to=${SANDBOX_TO}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/billing/{path}":{"get":{"tags":["Billing"],"summary":"Call a retired billing path","description":"Any method on any other path under /v1/billing answers 410: send the same request to https://id.tangle.tools with your Platform API key.","parameters":[{"schema":{"type":"string"},"required":true,"name":"path","in":"path"}],"responses":{"410":{"description":"Retired path (BILLING_MOVED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/billing/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/billing/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/billing/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/billing/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/environments":{"get":{"tags":["Templates"],"security":[{},{"BearerAuth":[]}],"summary":"List environments","description":"The credential is optional. Without one, the list holds the system environments. With one, it also holds the caller's templates; a failed template lookup leaves them out without an error.","responses":{"200":{"description":"Environments a sandbox can start from","content":{"application/json":{"schema":{"type":"object","properties":{"environments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"universal, or template:<templateId> for one of the caller's templates."},"description":{"type":"string"},"version":{"type":"string","enum":["latest","snapshot"]},"runtimes":{"type":"array","items":{"type":"string","enum":["docker","firecracker"]}},"templateId":{"type":"string"},"snapshotId":{"type":"string"}},"required":["id","description","version","runtimes"]}}},"required":["environments"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/environments`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/environments`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/environments\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/environments\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets":{"get":{"tags":["Fleets"],"summary":"List sandbox fleets","responses":{"200":{"description":"Fleets","content":{"application/json":{"schema":{"type":"object","properties":{"fleets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"parent":{"type":"object","properties":{"fleetId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"agentId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}},"required":["fleetId","agentId"],"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"capacityReservation":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["reserved","released"]},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"machineCount":{"type":"integer","minimum":0},"createdAt":{"type":"string"},"releasedAt":{"type":"string"},"releaseReason":{"type":"string","enum":["rollback","delete","machine_delete","reconcile"]}},"required":["id","status","resources","machineCount","createdAt"]},"workspace":{"type":"object","properties":{"mode":{"type":"string","enum":["isolated","shared"]},"id":{"type":"string"},"quotaMb":{"type":"integer","exclusiveMinimum":0},"snapshotId":{"type":"string","minLength":1,"maxLength":128},"mountPath":{"type":"string","minLength":1,"maxLength":256},"status":{"type":"string","enum":["creating","ready","deleted"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"deletedAt":{"type":"string"}},"required":["mode","id","status","createdAt","updatedAt"]},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"machineRuntimeMs":{"type":"object","additionalProperties":{"type":"number","minimum":0}},"updatedAt":{"type":"string"}},"required":["runtimeMs","machineRuntimeMs","updatedAt"]},"machines":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string"},"templateId":{"type":"string"},"publicTemplateId":{"type":"string"},"acceleratorCount":{"type":"integer","minimum":0},"workspaceMountPath":{"type":"string"},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"updatedAt":{"type":"string"}},"required":["runtimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","machineId","sandboxId","status","createdAt","updatedAt"],"additionalProperties":false}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","metadata","machines","createdAt","updatedAt"]}}},"required":["fleets"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/fleets`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Fleets"],"summary":"Create a sandbox fleet record","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"status":{"type":"string","enum":["creating","running"]},"metadata":{"type":"object","additionalProperties":{}},"workspace":{"type":"object","properties":{"mode":{"type":"string","enum":["isolated","shared"]},"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"quotaMb":{"type":"integer","exclusiveMinimum":0},"snapshotId":{"type":"string","minLength":1,"maxLength":128},"mountPath":{"type":"string","minLength":1,"maxLength":256}},"required":["mode"]},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"parent":{"type":"object","properties":{"fleetId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"agentId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}},"required":["fleetId","agentId"],"additionalProperties":false},"machines":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"sandboxId":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string","minLength":1,"maxLength":512},"templateId":{"type":"string","minLength":1,"maxLength":256},"publicTemplateId":{"type":"string","minLength":1,"maxLength":256},"acceleratorCount":{"type":"integer","minimum":0},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}}},"required":["machineId","sandboxId"],"additionalProperties":false},"minItems":1,"maxItems":1000}},"required":["id","machines"],"additionalProperties":false}}}},"responses":{"201":{"description":"Fleet created","content":{"application/json":{"schema":{"type":"object","properties":{"fleet":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"parent":{"type":"object","properties":{"fleetId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"agentId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}},"required":["fleetId","agentId"],"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"capacityReservation":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["reserved","released"]},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"machineCount":{"type":"integer","minimum":0},"createdAt":{"type":"string"},"releasedAt":{"type":"string"},"releaseReason":{"type":"string","enum":["rollback","delete","machine_delete","reconcile"]}},"required":["id","status","resources","machineCount","createdAt"]},"workspace":{"type":"object","properties":{"mode":{"type":"string","enum":["isolated","shared"]},"id":{"type":"string"},"quotaMb":{"type":"integer","exclusiveMinimum":0},"snapshotId":{"type":"string","minLength":1,"maxLength":128},"mountPath":{"type":"string","minLength":1,"maxLength":256},"status":{"type":"string","enum":["creating","ready","deleted"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"deletedAt":{"type":"string"}},"required":["mode","id","status","createdAt","updatedAt"]},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"machineRuntimeMs":{"type":"object","additionalProperties":{"type":"number","minimum":0}},"updatedAt":{"type":"string"}},"required":["runtimeMs","machineRuntimeMs","updatedAt"]},"machines":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string"},"templateId":{"type":"string"},"publicTemplateId":{"type":"string"},"acceleratorCount":{"type":"integer","minimum":0},"workspaceMountPath":{"type":"string"},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"updatedAt":{"type":"string"}},"required":["runtimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","machineId","sandboxId","status","createdAt","updatedAt"],"additionalProperties":false}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","metadata","machines","createdAt","updatedAt"]}},"required":["fleet"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Sandbox service verification failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/capabilities":{"get":{"tags":["Fleets"],"summary":"List fleet driver capabilities","responses":{"200":{"description":"Fleet driver capabilities","content":{"application/json":{"schema":{"type":"object","properties":{"drivers":{"type":"array","items":{"type":"object","properties":{"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"sharedWorkspace":{"type":"boolean"},"accelerators":{"type":"boolean"},"queueTimings":{"type":"boolean"}},"required":["driverType","sharedWorkspace","accelerators","queueTimings"]}}},"required":["drivers"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/fleets/capabilities`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/capabilities`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets/capabilities\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/capabilities\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/estimate":{"post":{"tags":["Fleets"],"summary":"Estimate sandbox fleet cost before create","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"machineCount":{"type":"integer","exclusiveMinimum":0},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0}},"required":["machineCount"],"additionalProperties":false}}}},"responses":{"200":{"description":"Fleet cost estimate","content":{"application/json":{"schema":{"type":"object","properties":{"estimate":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro","enterprise"]},"currency":{"type":"string","enum":["USD"]},"hourlyUsd":{"type":"number"},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"estimatedMaxLifetimeUsd":{"type":"number"},"requestedResources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"rates":{"type":"object","properties":{"cpuPerHr":{"type":"number"},"ramPerGbHr":{"type":"number"},"diskPerGbHr":{"type":"number"},"acceleratorPerDeviceHr":{"type":"number"},"minChargePerHr":{"type":"number"},"entDiscount":{"type":"number"}},"required":["cpuPerHr","ramPerGbHr","diskPerGbHr","acceleratorPerDeviceHr","minChargePerHr","entDiscount"]}},"required":["plan","currency","hourlyUsd","maxLifetimeSeconds","estimatedMaxLifetimeUsd","requestedResources","rates"]}},"required":["estimate"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/estimate`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/estimate`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets/estimate\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/estimate\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/operations":{"get":{"tags":["Fleets"],"summary":"Summarize fleet operational risk for the current owner","responses":{"200":{"description":"Fleet operations summary","content":{"application/json":{"schema":{"type":"object","properties":{"capacity":{"type":"object","properties":{"fleets":{"type":"integer","minimum":0},"machines":{"type":"integer","minimum":0},"runningMachines":{"type":"integer","minimum":0},"failedMachines":{"type":"integer","minimum":0},"requestedCpu":{"type":"number","minimum":0},"requestedMemoryMb":{"type":"number","minimum":0},"requestedStorageMb":{"type":"number","minimum":0},"requestedAccelerators":{"type":"number","minimum":0}},"required":["fleets","machines","runningMachines","failedMachines","requestedCpu","requestedMemoryMb","requestedStorageMb","requestedAccelerators"]},"alerts":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"severity":{"type":"string","enum":["info","warn","critical"]},"fleetId":{"type":"string"},"machineId":{"type":"string"},"message":{"type":"string"},"runbook":{"type":"array","items":{"type":"string"}}},"required":["name","severity","message","runbook"]}}},"required":["capacity","alerts"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/fleets/operations`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/operations`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets/operations\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/operations\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/reap-expired":{"post":{"tags":["Fleets"],"summary":"Delete expired sandbox fleets for the current owner","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Expired fleet cleanup results","content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean"},"expired":{"type":"number"},"deleted":{"type":"number"},"fleets":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"expiredAt":{"type":"string"},"deleted":{"type":"boolean"},"machines":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"ok":{"type":"boolean"},"status":{"type":"number"},"error":{"type":"string"}},"required":["machineId","sandboxId","ok"]}}},"required":["fleetId","expiredAt","deleted","machines"]}}},"required":["dryRun","expired","deleted","fleets"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/reap-expired`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/reap-expired`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets/reap-expired\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/reap-expired\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/reconcile":{"post":{"tags":["Fleets"],"summary":"Find fleet machine records whose sandbox no longer exists","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Fleet reconciliation result","content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean"},"checked":{"type":"integer","minimum":0},"orphaned":{"type":"integer","minimum":0},"removed":{"type":"integer","minimum":0},"machines":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"removed":{"type":"boolean"},"status":{"type":"number"},"error":{"type":"string"}},"required":["fleetId","machineId","sandboxId","removed"]}}},"required":["dryRun","checked","orphaned","removed","machines"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/reconcile`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/fleets/reconcile`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/fleets/reconcile\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/reconcile\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/{fleetId}":{"get":{"tags":["Fleets"],"summary":"Get a sandbox fleet","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Fleet","content":{"application/json":{"schema":{"type":"object","properties":{"fleet":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"parent":{"type":"object","properties":{"fleetId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"agentId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}},"required":["fleetId","agentId"],"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"capacityReservation":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["reserved","released"]},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"machineCount":{"type":"integer","minimum":0},"createdAt":{"type":"string"},"releasedAt":{"type":"string"},"releaseReason":{"type":"string","enum":["rollback","delete","machine_delete","reconcile"]}},"required":["id","status","resources","machineCount","createdAt"]},"workspace":{"type":"object","properties":{"mode":{"type":"string","enum":["isolated","shared"]},"id":{"type":"string"},"quotaMb":{"type":"integer","exclusiveMinimum":0},"snapshotId":{"type":"string","minLength":1,"maxLength":128},"mountPath":{"type":"string","minLength":1,"maxLength":256},"status":{"type":"string","enum":["creating","ready","deleted"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"deletedAt":{"type":"string"}},"required":["mode","id","status","createdAt","updatedAt"]},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"machineRuntimeMs":{"type":"object","additionalProperties":{"type":"number","minimum":0}},"updatedAt":{"type":"string"}},"required":["runtimeMs","machineRuntimeMs","updatedAt"]},"machines":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string"},"templateId":{"type":"string"},"publicTemplateId":{"type":"string"},"acceleratorCount":{"type":"integer","minimum":0},"workspaceMountPath":{"type":"string"},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"updatedAt":{"type":"string"}},"required":["runtimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","machineId","sandboxId","status","createdAt","updatedAt"],"additionalProperties":false}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","metadata","machines","createdAt","updatedAt"]}},"required":["fleet"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"tags":["Fleets"],"summary":"Delete a sandbox fleet record","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Fleet deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"removal":{"type":"string","enum":["complete"]}},"required":["success"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Fleet machine deprovision failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/cost":{"get":{"tags":["Fleets"],"summary":"Estimate current sandbox fleet cost","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Fleet cost estimate","content":{"application/json":{"schema":{"type":"object","properties":{"estimate":{"type":"object","properties":{"plan":{"type":"string","enum":["free","pro","enterprise"]},"currency":{"type":"string","enum":["USD"]},"hourlyUsd":{"type":"number"},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"estimatedMaxLifetimeUsd":{"type":"number"},"requestedResources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"rates":{"type":"object","properties":{"cpuPerHr":{"type":"number"},"ramPerGbHr":{"type":"number"},"diskPerGbHr":{"type":"number"},"acceleratorPerDeviceHr":{"type":"number"},"minChargePerHr":{"type":"number"},"entDiscount":{"type":"number"}},"required":["cpuPerHr","ramPerGbHr","diskPerGbHr","acceleratorPerDeviceHr","minChargePerHr","entDiscount"]}},"required":["plan","currency","hourlyUsd","maxLifetimeSeconds","estimatedMaxLifetimeUsd","requestedResources","rates"]}},"required":["estimate"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/cost`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/cost`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/cost\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/cost\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/dispatch":{"post":{"tags":["Fleets"],"summary":"Dispatch a command to fleet machines","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"bufferResults":{"type":"boolean"},"machines":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"minItems":1,"maxItems":1000},"maxConcurrent":{"type":"integer","exclusiveMinimum":0,"maximum":64},"retry":{"type":"object","properties":{"attempts":{"type":"integer","minimum":1,"maximum":5}}},"type":{"type":"string","enum":["exec"]},"command":{"type":"string","minLength":1,"maxLength":8192},"cwd":{"type":"string"},"env":{"type":"object","additionalProperties":{"type":"string"}},"timeoutMs":{"type":"integer","minimum":100,"maximum":600000}},"required":["type","command"],"additionalProperties":false},{"type":"object","properties":{"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"bufferResults":{"type":"boolean"},"machines":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"minItems":1,"maxItems":1000},"maxConcurrent":{"type":"integer","exclusiveMinimum":0,"maximum":64},"retry":{"type":"object","properties":{"attempts":{"type":"integer","minimum":1,"maximum":5}}},"type":{"type":"string","enum":["prompt"]},"parts":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["text"]},"text":{"type":"string","minLength":1},"mediaType":{"type":"string"}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["file"]},"filename":{"type":"string","minLength":1},"mediaType":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["type","filename","url"],"additionalProperties":false}]},"minItems":1,"maxItems":64},"sessionId":{"type":"string","minLength":1,"maxLength":128},"backend":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["api","cli"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"authMode":{"type":"string","enum":["api-key","oauth"]},"authFiles":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":4096},"content":{"type":"string","maxLength":1048576},"mode":{"type":"integer","minimum":0,"maximum":511}},"required":["path","content"],"additionalProperties":false},"maxItems":128},"apiKeyEnv":{"type":"string","maxLength":128,"pattern":"^[A-Z_][A-Z0-9_]*$"}},"additionalProperties":false},"server":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"hostname":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}}},"required":["mcp"],"additionalProperties":false},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","properties":{"containerType":{"type":"string","minLength":1,"maxLength":256},"profileSecretIsolation":{"type":"boolean","const":true},"isolationPrincipal":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"traceAttributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"additionalProperties":{"type":"string","maxLength":1024}},"telemetry":{"not":{}}},"additionalProperties":false},"profileSha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":false},"context":{"type":"object","additionalProperties":{}},"timeoutMs":{"type":"integer","minimum":100,"maximum":3600000}},"required":["type","parts"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Dispatch results","content":{"application/json":{"schema":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"type":{"type":"string","enum":["exec","prompt"]},"results":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"ok":{"type":"boolean"},"durationMs":{"type":"number"},"attempts":{"type":"integer","exclusiveMinimum":0},"result":{"type":"object","properties":{"exitCode":{"type":"number"},"stdout":{"type":"string"},"stderr":{"type":"string"}},"required":["exitCode","stdout","stderr"]},"prompt":{"type":"object","properties":{"success":{"type":"boolean"},"response":{"type":"string"},"error":{"type":"string"},"traceId":{"type":"string"},"usage":{"type":"object","properties":{"inputTokens":{"type":"number"},"outputTokens":{"type":"number"}},"required":["inputTokens","outputTokens"]},"metadata":{"type":"object","additionalProperties":{}}},"required":["success"]},"error":{"type":"object","properties":{"message":{"type":"string"},"status":{"type":"number"},"failureClass":{"type":"string","enum":["oom","timeout","dependency","infra","model","user_code"]},"retryAfterMs":{"type":"integer","minimum":0}},"required":["message"]}},"required":["machineId","sandboxId","ok","durationMs"]}},"durationMs":{"type":"number"},"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.trace.v1"]},"traceId":{"type":"string"},"fleetId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"machineObservedLifecycleMs":{"type":"number","minimum":0},"dispatchFanoutMs":{"type":"number","minimum":0},"dispatchRuntimeMs":{"type":"number","minimum":0},"cleanupObservedMs":{"type":"number","minimum":0},"driverQueueMs":{"type":"number","minimum":0},"driverPlacementMs":{"type":"number","minimum":0},"driverProvisionMs":{"type":"number","minimum":0},"driverStartupMs":{"type":"number","minimum":0},"driverCleanupMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","machineObservedLifecycleMs","dispatchFanoutMs","dispatchRuntimeMs","cleanupObservedMs","driverQueueMs","driverPlacementMs","driverProvisionMs","driverStartupMs","driverCleanupMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0},"machineId":{"type":"string"}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["fleet.lifecycle.snapshot","fleet.machine.lifecycle.snapshot","fleet.workspace.lifecycle.snapshot","fleet.usage.snapshot","fleet.dispatch.result","fleet.insight.summary"]},"timestamp":{"type":"string"},"fleetId":{"type":"string"},"machineId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","fleetId","attributes"]}}},"required":["schemaVersion","traceId","fleetId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox_fleet"]},"fleetId":{"type":"string"}},"required":["type","fleetId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["fleetId","dispatchId","type","results","durationMs","trace","intelligence"]}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"PROFILE_SECRET_ISOLATION_UNAVAILABLE: backend.metadata.profileSecretIsolation needs every selected machine's runtime to seal the secret launch","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Dispatch conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"503":{"description":"PROFILE_SECRET_ISOLATION_UNAVAILABLE with reason sidecar_unreachable: a selected machine's runtime could not be asked; retry after Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/dispatch\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/dispatch\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/{fleetId}/dispatch/stream":{"post":{"tags":["Fleets"],"summary":"Stream a command dispatch to fleet machines","description":"Takes the same body as POST /v1/fleets/{fleetId}/dispatch and answers as server-sent events while the machines run. Each machine emits fleet.dispatch.branch.started and fleet.dispatch.branch.finished. The stream ends with fleet.dispatch.finished or fleet.dispatch.error. A failure before the stream starts is a JSON error instead. A dropped connection cancels the machines that have not started.","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"bufferResults":{"type":"boolean"},"machines":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"minItems":1,"maxItems":1000},"maxConcurrent":{"type":"integer","exclusiveMinimum":0,"maximum":64},"retry":{"type":"object","properties":{"attempts":{"type":"integer","minimum":1,"maximum":5}}},"type":{"type":"string","enum":["exec"]},"command":{"type":"string","minLength":1,"maxLength":8192},"cwd":{"type":"string"},"env":{"type":"object","additionalProperties":{"type":"string"}},"timeoutMs":{"type":"integer","minimum":100,"maximum":600000}},"required":["type","command"],"additionalProperties":false},{"type":"object","properties":{"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"bufferResults":{"type":"boolean"},"machines":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"minItems":1,"maxItems":1000},"maxConcurrent":{"type":"integer","exclusiveMinimum":0,"maximum":64},"retry":{"type":"object","properties":{"attempts":{"type":"integer","minimum":1,"maximum":5}}},"type":{"type":"string","enum":["prompt"]},"parts":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["text"]},"text":{"type":"string","minLength":1},"mediaType":{"type":"string"}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["file"]},"filename":{"type":"string","minLength":1},"mediaType":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["type","filename","url"],"additionalProperties":false}]},"minItems":1,"maxItems":64},"sessionId":{"type":"string","minLength":1,"maxLength":128},"backend":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["api","cli"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"authMode":{"type":"string","enum":["api-key","oauth"]},"authFiles":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":4096},"content":{"type":"string","maxLength":1048576},"mode":{"type":"integer","minimum":0,"maximum":511}},"required":["path","content"],"additionalProperties":false},"maxItems":128},"apiKeyEnv":{"type":"string","maxLength":128,"pattern":"^[A-Z_][A-Z0-9_]*$"}},"additionalProperties":false},"server":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"hostname":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}}},"required":["mcp"],"additionalProperties":false},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","properties":{"containerType":{"type":"string","minLength":1,"maxLength":256},"profileSecretIsolation":{"type":"boolean","const":true},"isolationPrincipal":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"traceAttributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"additionalProperties":{"type":"string","maxLength":1024}},"telemetry":{"not":{}}},"additionalProperties":false},"profileSha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"additionalProperties":false},"context":{"type":"object","additionalProperties":{}},"timeoutMs":{"type":"integer","minimum":100,"maximum":3600000}},"required":["type","parts"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Server-sent events (text/event-stream). Each frame has an event name and a JSON data field, modeled by the schema.","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/FleetDispatchStreamEvent"}}}},"400":{"description":"Invalid fleet ID or dispatch body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"402":{"description":"The account that pays for the fleet has recorded debt (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The fleet token is not scoped to this fleet or lacks the exec or prompt action, or the backend's profile secret isolation is unavailable (FORBIDDEN, PROFILE_SECRET_ISOLATION_UNAVAILABLE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Fleet not found (NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"A dispatch with this dispatchId is already active (CONFLICT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The paying account's balance is below the admission floor (QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"A selected machine could not be asked to prove secret isolation; retry after Retry-After (PROFILE_SECRET_ISOLATION_UNAVAILABLE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/stream`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/stream`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/dispatch/stream\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/dispatch/stream\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/{fleetId}/dispatch/{dispatchId}/cancel":{"post":{"tags":["Fleets"],"summary":"Cancel queued fleet dispatch branches","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"dispatchId","in":"path"}],"responses":{"202":{"description":"Dispatch cancel requested","content":{"application/json":{"schema":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"cancelled":{"type":"boolean"}},"required":["fleetId","dispatchId","cancelled"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst dispatchId = process.env.SANDBOX_DISPATCH_ID;\nif (!dispatchId) throw new Error(\"Set SANDBOX_DISPATCH_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/${encodeURIComponent(dispatchId)}/cancel`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst dispatchId = process.env.SANDBOX_DISPATCH_ID;\nif (!dispatchId) throw new Error(\"Set SANDBOX_DISPATCH_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/${encodeURIComponent(dispatchId)}/cancel`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\n_dispatchId = os.environ.get(\"SANDBOX_DISPATCH_ID\")\nif not _dispatchId: raise SystemExit(\"Set SANDBOX_DISPATCH_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/dispatch/{quote(_dispatchId or '', safe='')}/cancel\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/dispatch/${SANDBOX_DISPATCH_ID:?Set SANDBOX_DISPATCH_ID}/cancel\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/dispatch/{dispatchId}/results":{"get":{"tags":["Fleets"],"summary":"Read buffered fleet dispatch results","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"dispatchId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"required":false,"name":"machine","in":"query"}],"responses":{"200":{"description":"Buffered dispatch results","content":{"application/json":{"schema":{"type":"object","properties":{"fleetId":{"type":"string"},"dispatchId":{"type":"string"},"results":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"ok":{"type":"boolean"},"durationMs":{"type":"number"},"attempts":{"type":"integer","exclusiveMinimum":0},"result":{"type":"object","properties":{"exitCode":{"type":"number"},"stdout":{"type":"string"},"stderr":{"type":"string"}},"required":["exitCode","stdout","stderr"]},"prompt":{"type":"object","properties":{"success":{"type":"boolean"},"response":{"type":"string"},"error":{"type":"string"},"traceId":{"type":"string"},"usage":{"type":"object","properties":{"inputTokens":{"type":"number"},"outputTokens":{"type":"number"}},"required":["inputTokens","outputTokens"]},"metadata":{"type":"object","additionalProperties":{}}},"required":["success"]},"error":{"type":"object","properties":{"message":{"type":"string"},"status":{"type":"number"},"failureClass":{"type":"string","enum":["oom","timeout","dependency","infra","model","user_code"]},"retryAfterMs":{"type":"integer","minimum":0}},"required":["message"]}},"required":["machineId","sandboxId","ok","durationMs"]}},"cursor":{"type":"string"},"nextCursor":{"type":"string"},"done":{"type":"boolean"},"truncated":{"type":"boolean"},"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.trace.v1"]},"traceId":{"type":"string"},"fleetId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"machineObservedLifecycleMs":{"type":"number","minimum":0},"dispatchFanoutMs":{"type":"number","minimum":0},"dispatchRuntimeMs":{"type":"number","minimum":0},"cleanupObservedMs":{"type":"number","minimum":0},"driverQueueMs":{"type":"number","minimum":0},"driverPlacementMs":{"type":"number","minimum":0},"driverProvisionMs":{"type":"number","minimum":0},"driverStartupMs":{"type":"number","minimum":0},"driverCleanupMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","machineObservedLifecycleMs","dispatchFanoutMs","dispatchRuntimeMs","cleanupObservedMs","driverQueueMs","driverPlacementMs","driverProvisionMs","driverStartupMs","driverCleanupMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0},"machineId":{"type":"string"}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["fleet.lifecycle.snapshot","fleet.machine.lifecycle.snapshot","fleet.workspace.lifecycle.snapshot","fleet.usage.snapshot","fleet.dispatch.result","fleet.insight.summary"]},"timestamp":{"type":"string"},"fleetId":{"type":"string"},"machineId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","fleetId","attributes"]}}},"required":["schemaVersion","traceId","fleetId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox_fleet"]},"fleetId":{"type":"string"}},"required":["type","fleetId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["fleetId","dispatchId","results","done","trace","intelligence"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst dispatchId = process.env.SANDBOX_DISPATCH_ID;\nif (!dispatchId) throw new Error(\"Set SANDBOX_DISPATCH_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/${encodeURIComponent(dispatchId)}/results`, baseUrl);\nconst cursor = process.env.SANDBOX_CURSOR;\nif (cursor !== undefined) url.searchParams.set(\"cursor\", cursor);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst machine = process.env.SANDBOX_MACHINE;\nif (machine !== undefined) url.searchParams.set(\"machine\", machine);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst dispatchId = process.env.SANDBOX_DISPATCH_ID;\nif (!dispatchId) throw new Error(\"Set SANDBOX_DISPATCH_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/dispatch/${encodeURIComponent(dispatchId)}/results`, baseUrl);\nconst cursor = process.env.SANDBOX_CURSOR;\nif (cursor !== undefined) url.searchParams.set(\"cursor\", cursor);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst machine = process.env.SANDBOX_MACHINE;\nif (machine !== undefined) url.searchParams.set(\"machine\", machine);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\n_dispatchId = os.environ.get(\"SANDBOX_DISPATCH_ID\")\nif not _dispatchId: raise SystemExit(\"Set SANDBOX_DISPATCH_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/dispatch/{quote(_dispatchId or '', safe='')}/results\"\nquery = {}\n_cursor = os.environ.get(\"SANDBOX_CURSOR\")\nif _cursor is not None: query[\"cursor\"] = _cursor\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_machine = os.environ.get(\"SANDBOX_MACHINE\")\nif _machine is not None: query[\"machine\"] = _machine\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/dispatch/${SANDBOX_DISPATCH_ID:?Set SANDBOX_DISPATCH_ID}/results\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_CURSOR:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}cursor=${SANDBOX_CURSOR}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_MACHINE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}machine=${SANDBOX_MACHINE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/machines":{"post":{"tags":["Fleets"],"summary":"Add a machine to a sandbox fleet record","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"sandboxId":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string","minLength":1,"maxLength":512},"templateId":{"type":"string","minLength":1,"maxLength":256},"publicTemplateId":{"type":"string","minLength":1,"maxLength":256},"acceleratorCount":{"type":"integer","minimum":0},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}}},"required":["machineId","sandboxId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Machine added","content":{"application/json":{"schema":{"type":"object","properties":{"machine":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string"},"templateId":{"type":"string"},"publicTemplateId":{"type":"string"},"acceleratorCount":{"type":"integer","minimum":0},"workspaceMountPath":{"type":"string"},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"updatedAt":{"type":"string"}},"required":["runtimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","machineId","sandboxId","status","createdAt","updatedAt"],"additionalProperties":false}},"required":["machine"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Sandbox service verification failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/machines`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/machines`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/machines\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/machines\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/{fleetId}/machines/{machineId}":{"delete":{"tags":["Fleets"],"summary":"Remove a machine from a sandbox fleet record","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"machineId","in":"path"}],"responses":{"200":{"description":"Machine removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"Fleet machine deprovision failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst machineId = process.env.SANDBOX_MACHINE_ID;\nif (!machineId) throw new Error(\"Set SANDBOX_MACHINE_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/machines/${encodeURIComponent(machineId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst machineId = process.env.SANDBOX_MACHINE_ID;\nif (!machineId) throw new Error(\"Set SANDBOX_MACHINE_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/machines/${encodeURIComponent(machineId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\n_machineId = os.environ.get(\"SANDBOX_MACHINE_ID\")\nif not _machineId: raise SystemExit(\"Set SANDBOX_MACHINE_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/machines/{quote(_machineId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/machines/${SANDBOX_MACHINE_ID:?Set SANDBOX_MACHINE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/manifest":{"get":{"tags":["Fleets"],"summary":"Get a fleet manifest for coordinator workflows","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Fleet manifest","content":{"application/json":{"schema":{"type":"object","properties":{"fleetId":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"workspace":{"type":"object","properties":{"mode":{"type":"string","enum":["isolated","shared"]},"id":{"type":"string"},"quotaMb":{"type":"integer","exclusiveMinimum":0},"snapshotId":{"type":"string","minLength":1,"maxLength":128},"mountPath":{"type":"string","minLength":1,"maxLength":256},"status":{"type":"string","enum":["creating","ready","deleted"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"deletedAt":{"type":"string"}},"required":["mode","id","status","createdAt","updatedAt"]},"machines":{"type":"array","items":{"type":"object","properties":{"fleetId":{"type":"string"},"machineId":{"type":"string"},"sandboxId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"role":{"type":"string","enum":["coordinator","worker"]},"driverType":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"environment":{"type":"string"},"templateId":{"type":"string"},"publicTemplateId":{"type":"string"},"acceleratorCount":{"type":"integer","minimum":0},"workspaceMountPath":{"type":"string"},"driverTimings":{"type":"object","properties":{"queueMs":{"type":"number","minimum":0},"placementMs":{"type":"number","minimum":0},"provisionMs":{"type":"number","minimum":0},"startupMs":{"type":"number","minimum":0},"cleanupMs":{"type":"number","minimum":0}}},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"updatedAt":{"type":"string"}},"required":["runtimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","machineId","sandboxId","status","createdAt","updatedAt"],"additionalProperties":false}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","metadata","machines","createdAt","updatedAt"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/manifest`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/manifest`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/manifest\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/manifest\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/token":{"post":{"tags":["Fleets"],"summary":"Mint a fleet-scoped agent token","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"string","enum":["list","create","delete","exec","prompt","read","write"]},"minItems":1},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false},"ttlMinutes":{"type":"integer","minimum":1,"maximum":720}},"additionalProperties":false}}}},"responses":{"200":{"description":"Fleet-scoped token","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"expiresAt":{"type":"number"},"fleetId":{"type":"string"},"actions":{"type":"array","items":{"type":"string","enum":["list","create","delete","exec","prompt","read","write"]}},"policy":{"type":"object","properties":{"maxMachines":{"type":"integer","exclusiveMinimum":0},"maxConcurrentCreates":{"type":"integer","exclusiveMinimum":0},"maxTotalCpu":{"type":"number","minimum":0},"maxTotalMemoryMb":{"type":"number","minimum":0},"maxTotalStorageMb":{"type":"number","minimum":0},"maxTotalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0},"maxSpendUsd":{"type":"number","minimum":0},"allowedDrivers":{"type":"array","items":{"type":"string","enum":["docker","firecracker","host-agent","tangle"]},"minItems":1},"allowedEnvironments":{"type":"array","items":{"type":"string","minLength":1,"maxLength":512},"minItems":1},"allowedTemplateIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1},"allowAccelerators":{"type":"boolean"}},"additionalProperties":false}},"required":["token","expiresAt","fleetId","actions"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Fleet expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/token`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/token`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/token\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/token\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/fleets/{fleetId}/trace":{"get":{"tags":["Fleets"],"summary":"Export fleet trace and intelligence envelope","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"includeIntelligence","in":"query"}],"responses":{"200":{"description":"Fleet trace export","content":{"application/json":{"schema":{"type":"object","properties":{"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.trace.v1"]},"traceId":{"type":"string"},"fleetId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"machineObservedLifecycleMs":{"type":"number","minimum":0},"dispatchFanoutMs":{"type":"number","minimum":0},"dispatchRuntimeMs":{"type":"number","minimum":0},"cleanupObservedMs":{"type":"number","minimum":0},"driverQueueMs":{"type":"number","minimum":0},"driverPlacementMs":{"type":"number","minimum":0},"driverProvisionMs":{"type":"number","minimum":0},"driverStartupMs":{"type":"number","minimum":0},"driverCleanupMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","machineObservedLifecycleMs","dispatchFanoutMs","dispatchRuntimeMs","cleanupObservedMs","driverQueueMs","driverPlacementMs","driverProvisionMs","driverStartupMs","driverCleanupMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0},"machineId":{"type":"string"}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["fleet.lifecycle.snapshot","fleet.machine.lifecycle.snapshot","fleet.workspace.lifecycle.snapshot","fleet.usage.snapshot","fleet.dispatch.result","fleet.insight.summary"]},"timestamp":{"type":"string"},"fleetId":{"type":"string"},"machineId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","fleetId","attributes"]}}},"required":["schemaVersion","traceId","fleetId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox_fleet"]},"fleetId":{"type":"string"}},"required":["type","fleetId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["trace"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/trace`, baseUrl);\nconst includeIntelligence = process.env.SANDBOX_INCLUDE_INTELLIGENCE;\nif (includeIntelligence !== undefined) url.searchParams.set(\"includeIntelligence\", includeIntelligence);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/trace`, baseUrl);\nconst includeIntelligence = process.env.SANDBOX_INCLUDE_INTELLIGENCE;\nif (includeIntelligence !== undefined) url.searchParams.set(\"includeIntelligence\", includeIntelligence);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/trace\"\nquery = {}\n_includeIntelligence = os.environ.get(\"SANDBOX_INCLUDE_INTELLIGENCE\")\nif _includeIntelligence is not None: query[\"includeIntelligence\"] = _includeIntelligence\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/trace\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_INCLUDE_INTELLIGENCE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}includeIntelligence=${SANDBOX_INCLUDE_INTELLIGENCE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/usage":{"get":{"tags":["Fleets"],"summary":"Get fleet usage and intelligence summary","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Fleet usage summary","content":{"application/json":{"schema":{"type":"object","properties":{"usage":{"type":"object","properties":{"fleetId":{"type":"string"},"status":{"type":"string","enum":["creating","running","degraded","deleting","deleted","failed"]},"machineCount":{"type":"number"},"coordinatorCount":{"type":"number"},"workerCount":{"type":"number"},"runningMachines":{"type":"number"},"failedMachines":{"type":"number"},"resources":{"type":"object","properties":{"machines":{"type":"integer","minimum":0},"totalCpu":{"type":"number","minimum":0},"totalMemoryMb":{"type":"number","minimum":0},"totalStorageMb":{"type":"number","minimum":0},"totalAccelerators":{"type":"number","minimum":0},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0}},"required":["machines","totalCpu","totalMemoryMb","totalStorageMb","totalAccelerators"]},"meteredUsage":{"type":"object","properties":{"runtimeMs":{"type":"number","minimum":0},"machineRuntimeMs":{"type":"object","additionalProperties":{"type":"number","minimum":0}},"updatedAt":{"type":"string"}},"required":["runtimeMs","machineRuntimeMs","updatedAt"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["fleetId","status","machineCount","coordinatorCount","workerCount","runningMachines","failedMachines","createdAt","updatedAt"]},"insights":{"type":"object","properties":{"reliabilityScore":{"type":"number"},"parallelismEfficiencyScore":{"type":"number"},"failureRate":{"type":"number"},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["reliabilityScore","parallelismEfficiencyScore","failureRate","recommendedActions"]},"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.trace.v1"]},"traceId":{"type":"string"},"fleetId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"machineObservedLifecycleMs":{"type":"number","minimum":0},"dispatchFanoutMs":{"type":"number","minimum":0},"dispatchRuntimeMs":{"type":"number","minimum":0},"cleanupObservedMs":{"type":"number","minimum":0},"driverQueueMs":{"type":"number","minimum":0},"driverPlacementMs":{"type":"number","minimum":0},"driverProvisionMs":{"type":"number","minimum":0},"driverStartupMs":{"type":"number","minimum":0},"driverCleanupMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","machineObservedLifecycleMs","dispatchFanoutMs","dispatchRuntimeMs","cleanupObservedMs","driverQueueMs","driverPlacementMs","driverProvisionMs","driverStartupMs","driverCleanupMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0},"machineId":{"type":"string"}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["fleet.lifecycle.snapshot","fleet.machine.lifecycle.snapshot","fleet.workspace.lifecycle.snapshot","fleet.usage.snapshot","fleet.dispatch.result","fleet.insight.summary"]},"timestamp":{"type":"string"},"fleetId":{"type":"string"},"machineId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","fleetId","attributes"]}}},"required":["schemaVersion","traceId","fleetId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["fleet.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox_fleet"]},"fleetId":{"type":"string"}},"required":["type","fleetId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["usage","insights","trace","intelligence"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/usage`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/usage`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/usage\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/usage\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/workspace/reconcile":{"post":{"tags":["Fleets"],"summary":"Check fleet machines against the shared workspace mount","description":"Compares each machine's recorded workspace mount path with the fleet's shared workspace mount path. It reads recorded state, contacts no machine and changes nothing. A fleet with no shared workspace mount path reports every machine as mounted. A fleet token needs the read action.","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Mount check","content":{"application/json":{"schema":{"type":"object","properties":{"fleetId":{"type":"string"},"workspaceId":{"type":"string"},"checked":{"type":"integer"},"orphanedMounts":{"type":"integer"},"machines":{"type":"array","items":{"type":"object","properties":{"machineId":{"type":"string"},"sandboxId":{"type":"string"},"mounted":{"type":"boolean"}},"required":["machineId","sandboxId","mounted"]}}},"required":["fleetId","checked","orphanedMounts","machines"]}}}},"400":{"description":"Invalid fleet ID (VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"The fleet token is not scoped to this fleet or lacks the action (FORBIDDEN), or the credential is restricted to its sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Fleet not found (NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/reconcile`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/reconcile`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/workspace/reconcile\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/workspace/reconcile\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/workspace/snapshots":{"post":{"tags":["Fleets"],"summary":"Snapshot the fleet workspace","description":"Acts on the workspace of the fleet's coordinator machine, or of its first machine when none is a coordinator. A fleet token needs the read action.","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"}],"responses":{"200":{"description":"Snapshot captured","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"snapshot":{"type":"object","properties":{"snapshotId":{"type":"string"},"id":{"type":"string"},"projectRef":{"type":"string"},"createdAt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"paths":{"type":"array","items":{"type":"string"}},"sizeBytes":{"type":"number"},"remoteSync":{"$ref":"#/components/schemas/SnapshotRemoteSync"}},"required":["snapshotId","id","projectRef","createdAt","tags","paths"]}},"required":["success","snapshot"]}}}},"400":{"description":"Invalid fleet or snapshot ID, or the sandbox service refused the request (VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"The fleet token is not scoped to this fleet or lacks the action (FORBIDDEN), or the credential is restricted to its sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Fleet not found, the fleet has no machines, or the sandbox service found no such sandbox or snapshot (NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"The sandbox service reported a conflict (CONFLICT)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"429":{"description":"The sandbox service rate-limited or refused the request for quota (RATE_LIMITED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"The sandbox service failed (UPSTREAM_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/snapshots`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/snapshots`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/workspace/snapshots\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/workspace/snapshots\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/fleets/{fleetId}/workspace/snapshots/{snapshotId}/restore":{"post":{"tags":["Fleets"],"summary":"Restore the fleet workspace from a snapshot","description":"Acts on the workspace of the fleet's coordinator machine, or of its first machine when none is a coordinator. A fleet token needs the write action.","parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},"required":true,"name":"fleetId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":128},"required":true,"name":"snapshotId","in":"path"}],"responses":{"200":{"description":"Snapshot restored","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"snapshot":{"type":"object","properties":{"snapshotId":{"type":"string"},"id":{"type":"string"},"projectRef":{"type":"string"},"createdAt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"paths":{"type":"array","items":{"type":"string"}},"sizeBytes":{"type":"number"},"remoteSync":{"$ref":"#/components/schemas/SnapshotRemoteSync"}},"required":["snapshotId","id","projectRef","createdAt","tags","paths"]}},"required":["success","snapshot"]}}}},"202":{"description":"Restore accepted; it runs as a background job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotRestoreAcceptedResponse"}}}},"400":{"description":"Invalid fleet or snapshot ID, or the sandbox service refused the request (VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"The fleet token is not scoped to this fleet or lacks the action (FORBIDDEN), or the credential is restricted to its sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Fleet not found, the fleet has no machines, or the sandbox service found no such sandbox or snapshot (NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"The sandbox service reported a conflict (CONFLICT)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"429":{"description":"The sandbox service rate-limited or refused the request for quota (RATE_LIMITED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"502":{"description":"The sandbox service failed (UPSTREAM_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/snapshots/${encodeURIComponent(snapshotId)}/restore`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst fleetId = process.env.SANDBOX_FLEET_ID;\nif (!fleetId) throw new Error(\"Set SANDBOX_FLEET_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/fleets/${encodeURIComponent(fleetId)}/workspace/snapshots/${encodeURIComponent(snapshotId)}/restore`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_fleetId = os.environ.get(\"SANDBOX_FLEET_ID\")\nif not _fleetId: raise SystemExit(\"Set SANDBOX_FLEET_ID\")\n_snapshotId = os.environ.get(\"SANDBOX_SNAPSHOT_ID\")\nif not _snapshotId: raise SystemExit(\"Set SANDBOX_SNAPSHOT_ID\")\nurl = f\"{base_url}/v1/fleets/{quote(_fleetId or '', safe='')}/workspace/snapshots/{quote(_snapshotId or '', safe='')}/restore\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/fleets/${SANDBOX_FLEET_ID:?Set SANDBOX_FLEET_ID}/workspace/snapshots/${SANDBOX_SNAPSHOT_ID:?Set SANDBOX_SNAPSHOT_ID}/restore\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/images":{"post":{"tags":["Images"],"summary":"Build an image","description":"Image builds are disabled. Every request is refused before its body is read.","responses":{"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"Image builds are disabled (IMAGE_BUILDS_DISABLED), or the credential is restricted to its own sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The image catalog did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/images`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/images`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/images\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/images\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"get":{"tags":["Images"],"summary":"List images","description":"Image builds are disabled, so the list is empty.","parameters":[{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"default":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string"},"required":false,"name":"label","in":"query"},{"schema":{"type":["boolean","null"],"default":false},"required":false,"name":"includeCache","in":"query"}],"responses":{"200":{"description":"The images","content":{"application/json":{"schema":{"type":"object","properties":{"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"tag":{"type":"string"},"size":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"labels":{"type":"object","additionalProperties":{"type":"string"}},"spec":{"type":"object","additionalProperties":{}},"digest":{"type":"string"},"inUse":{"type":"boolean"}},"required":["id","tag","size","createdAt","digest","inUse"]}},"total":{"type":"number"},"offset":{"type":"number"},"limit":{"type":"number"}},"required":["images","total","offset","limit"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The image catalog did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/images`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst label = process.env.SANDBOX_LABEL;\nif (label !== undefined) url.searchParams.set(\"label\", label);\nconst includeCache = process.env.SANDBOX_INCLUDE_CACHE;\nif (includeCache !== undefined) url.searchParams.set(\"includeCache\", includeCache);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/images`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst label = process.env.SANDBOX_LABEL;\nif (label !== undefined) url.searchParams.set(\"label\", label);\nconst includeCache = process.env.SANDBOX_INCLUDE_CACHE;\nif (includeCache !== undefined) url.searchParams.set(\"includeCache\", includeCache);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/images\"\nquery = {}\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_offset = os.environ.get(\"SANDBOX_OFFSET\")\nif _offset is not None: query[\"offset\"] = _offset\n_label = os.environ.get(\"SANDBOX_LABEL\")\nif _label is not None: query[\"label\"] = _label\n_includeCache = os.environ.get(\"SANDBOX_INCLUDE_CACHE\")\nif _includeCache is not None: query[\"includeCache\"] = _includeCache\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/images\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_OFFSET:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}offset=${SANDBOX_OFFSET}\"\nfi\nif [ -n \"${SANDBOX_LABEL:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}label=${SANDBOX_LABEL}\"\nfi\nif [ -n \"${SANDBOX_INCLUDE_CACHE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}includeCache=${SANDBOX_INCLUDE_CACHE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/images/{id}":{"get":{"tags":["Images"],"summary":"Get an image","description":"Image builds are disabled, so no image exists to read, delete or tag: every id answers 404.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No image has this id (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The image catalog did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/images/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/images/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"tags":["Images"],"summary":"Delete an image","description":"Image builds are disabled, so no image exists to read, delete or tag: every id answers 404.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":["boolean","null"],"default":false},"required":false,"name":"force","in":"query"}],"responses":{"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No image has this id (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The image catalog did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}`, baseUrl);\nconst force = process.env.SANDBOX_FORCE;\nif (force !== undefined) url.searchParams.set(\"force\", force);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}`, baseUrl);\nconst force = process.env.SANDBOX_FORCE;\nif (force !== undefined) url.searchParams.set(\"force\", force);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/images/{quote(_id or '', safe='')}\"\nquery = {}\n_force = os.environ.get(\"SANDBOX_FORCE\")\nif _force is not None: query[\"force\"] = _force\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/images/${SANDBOX_ID:?Set SANDBOX_ID}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_FORCE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}force=${SANDBOX_FORCE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/images/{id}/tag":{"post":{"tags":["Images"],"summary":"Tag an image","description":"Image builds are disabled, so no image exists to read, delete or tag: every id answers 404.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tag":{"type":"string"}},"required":["tag"]}}}},"responses":{"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No image has this id (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The image catalog did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}/tag`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/images/${encodeURIComponent(id)}/tag`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/images/{quote(_id or '', safe='')}/tag\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/images/${SANDBOX_ID:?Set SANDBOX_ID}/tag\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/instances/{key}":{"get":{"tags":["Instances"],"summary":"Get an instance","description":"An instance maps a key you choose to the sandbox that serves it now. Keys are scoped to the authenticated customer.","parameters":[{"schema":{"type":"string","description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit."},"required":true,"description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit.","name":"key","in":"path"}],"responses":{"200":{"description":"The instance record","content":{"application/json":{"schema":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/SandboxInstance"}},"required":["instance"]}}}},"400":{"description":"The key or the body is invalid (INVALID_INSTANCE_KEY, VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The key has no record (INSTANCE_NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_key = os.environ.get(\"SANDBOX_KEY\")\nif not _key: raise SystemExit(\"Set SANDBOX_KEY\")\nurl = f\"{base_url}/v1/instances/{quote(_key or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/instances/${SANDBOX_KEY:?Set SANDBOX_KEY}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"patch":{"tags":["Instances"],"summary":"Update an instance","description":"Changes the profile version that new sessions bind to, or marks or clears a failed start, on the sandbox of the given generation. A marked failed start keeps its first time. Send at least one of profileVersion and unstartable.","parameters":[{"schema":{"type":"string","description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit."},"required":true,"description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit.","name":"key","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"generation":{"type":"integer","minimum":1,"maximum":9007199254740991},"profileVersion":{"type":"string"},"unstartable":{"type":"string","enum":["mark","clear"]}},"required":["generation"],"additionalProperties":false}}}},"responses":{"200":{"description":"The updated record","content":{"application/json":{"schema":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/SandboxInstance"}},"required":["instance"]}}}},"400":{"description":"The key or the body is invalid (INVALID_INSTANCE_KEY, VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The key has no record (INSTANCE_NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"409":{"description":"The record changed since it was read; instance holds the current record (INSTANCE_CONFLICT)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_key = os.environ.get(\"SANDBOX_KEY\")\nif not _key: raise SystemExit(\"Set SANDBOX_KEY\")\nurl = f\"{base_url}/v1/instances/{quote(_key or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/instances/${SANDBOX_KEY:?Set SANDBOX_KEY}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Instances"],"summary":"Release an instance key","description":"Forgets the key. Its sandbox is left as it is.","parameters":[{"schema":{"type":"string","description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit."},"required":true,"description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit.","name":"key","in":"path"}],"responses":{"204":{"description":"The key was released"},"400":{"description":"The key or the body is invalid (INVALID_INSTANCE_KEY, VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The key has no record (INSTANCE_NOT_FOUND)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_key = os.environ.get(\"SANDBOX_KEY\")\nif not _key: raise SystemExit(\"Set SANDBOX_KEY\")\nurl = f\"{base_url}/v1/instances/{quote(_key or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/instances/${SANDBOX_KEY:?Set SANDBOX_KEY}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/instances/{key}/binding":{"put":{"tags":["Instances"],"summary":"Bind a sandbox to an instance","description":"Records the sandbox that serves the key for the given generation. A key with no record starts at generation 1. Binding the sandbox that is already bound succeeds and clears a marked failed start. A member-paid line key can bind only the isolated sandbox it created.","parameters":[{"schema":{"type":"string","description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit."},"required":true,"description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit.","name":"key","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"generation":{"type":"integer","minimum":1,"maximum":9007199254740991},"sandboxId":{"type":"string"},"profileVersion":{"type":"string"}},"required":["generation","sandboxId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The bound record","content":{"application/json":{"schema":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/SandboxInstance"}},"required":["instance"]}}}},"400":{"description":"The key or the body is invalid (INVALID_INSTANCE_KEY, VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, MEMBER_PAID_BOX_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The record changed since it was read; instance holds the current record (INSTANCE_CONFLICT)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"503":{"description":"Member sandbox authority could not be checked (MEMBER_PAID_BOX_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}/binding`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}/binding`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_key = os.environ.get(\"SANDBOX_KEY\")\nif not _key: raise SystemExit(\"Set SANDBOX_KEY\")\nurl = f\"{base_url}/v1/instances/{quote(_key or '', safe='')}/binding\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/instances/${SANDBOX_KEY:?Set SANDBOX_KEY}/binding\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/instances/{key}/replace":{"post":{"tags":["Instances"],"summary":"Replace an instance's sandbox","description":"Retires the key's sandbox of the given generation and opens the next generation with no sandbox. The retired sandbox is not deleted. A key with no record accepts generation 1 and starts at generation 2.","parameters":[{"schema":{"type":"string","description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit."},"required":true,"description":"1-200 characters: letters, digits and . _ : @ -, starting with a letter or digit.","name":"key","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"generation":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["generation"],"additionalProperties":false}}}},"responses":{"200":{"description":"The record at the next generation","content":{"application/json":{"schema":{"type":"object","properties":{"instance":{"$ref":"#/components/schemas/SandboxInstance"}},"required":["instance"]}}}},"400":{"description":"The key or the body is invalid (INVALID_INSTANCE_KEY, VALIDATION_ERROR)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The record changed since it was read; instance holds the current record (INSTANCE_CONFLICT)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["message","code"]},"instance":{"allOf":[{"$ref":"#/components/schemas/SandboxInstance"},{"type":["object","null"]}]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}/replace`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst key = process.env.SANDBOX_KEY;\nif (!key) throw new Error(\"Set SANDBOX_KEY\");\nconst url = new URL(`/v1/instances/${encodeURIComponent(key)}/replace`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_key = os.environ.get(\"SANDBOX_KEY\")\nif not _key: raise SystemExit(\"Set SANDBOX_KEY\")\nurl = f\"{base_url}/v1/instances/{quote(_key or '', safe='')}/replace\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/instances/${SANDBOX_KEY:?Set SANDBOX_KEY}/replace\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/intelligence/reports":{"post":{"tags":["Intelligence"],"summary":"Create an intelligence report","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string","minLength":1,"maxLength":256},"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"},"window":{"type":"object","properties":{"since":{"type":"integer","minimum":0,"maximum":8640000000000000},"until":{"type":"integer","minimum":0,"maximum":8640000000000000}}},"compareTo":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string","minLength":1,"maxLength":256},"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"}},"required":["type","id"]}},"required":["type","id"]},"mode":{"type":"string","enum":["deterministic","agentic"],"default":"deterministic"},"acknowledgeCost":{"type":"boolean"},"budget":{"type":"object","properties":{"maxUsd":{"type":"number","minimum":0},"billTo":{"type":"string","enum":["customer","platform"],"default":"customer"}}},"metadata":{"type":"object","additionalProperties":{}}},"required":["subject"]}}}},"responses":{"201":{"description":"Created intelligence report","content":{"application/json":{"schema":{"type":"object","properties":{"report":{"type":"object","properties":{"jobId":{"type":"string"},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string"},"dispatchId":{"type":"string"}},"required":["type","id"]},"mode":{"type":"string","enum":["deterministic","agentic"],"default":"deterministic"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean"},"billedTo":{"type":"string","enum":["platform","customer"]},"costUsd":{"type":"number"},"reason":{"type":"string"},"budgetMaxUsd":{"type":"number"}},"required":["billable","billedTo","costUsd","reason"]},"result":{"type":["object","null"],"additionalProperties":{}},"error":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string"}},"required":["jobId","subject","status","billing","result","createdAt","updatedAt"]}},"required":["report"]}}}},"400":{"description":"Invalid request"},"402":{"description":"Payment required"}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/intelligence/reports`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/intelligence/reports`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/intelligence/reports\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/intelligence/reports\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"get":{"tags":["Intelligence"],"summary":"List intelligence reports","parameters":[{"schema":{"type":"string","enum":["sandbox","fleet"]},"required":false,"name":"subjectType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"subjectId","in":"query"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Intelligence reports","content":{"application/json":{"schema":{"type":"object","properties":{"reports":{"type":"array","items":{"type":"object","properties":{"jobId":{"type":"string"},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string"},"dispatchId":{"type":"string"}},"required":["type","id"]},"mode":{"type":"string","enum":["deterministic","agentic"],"default":"deterministic"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean"},"billedTo":{"type":"string","enum":["platform","customer"]},"costUsd":{"type":"number"},"reason":{"type":"string"},"budgetMaxUsd":{"type":"number"}},"required":["billable","billedTo","costUsd","reason"]},"result":{"type":["object","null"],"additionalProperties":{}},"error":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string"}},"required":["jobId","subject","status","billing","result","createdAt","updatedAt"]}}},"required":["reports"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/intelligence/reports`, baseUrl);\nconst subjectType = process.env.SANDBOX_SUBJECT_TYPE;\nif (subjectType !== undefined) url.searchParams.set(\"subjectType\", subjectType);\nconst subjectId = process.env.SANDBOX_SUBJECT_ID;\nif (subjectId !== undefined) url.searchParams.set(\"subjectId\", subjectId);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/intelligence/reports`, baseUrl);\nconst subjectType = process.env.SANDBOX_SUBJECT_TYPE;\nif (subjectType !== undefined) url.searchParams.set(\"subjectType\", subjectType);\nconst subjectId = process.env.SANDBOX_SUBJECT_ID;\nif (subjectId !== undefined) url.searchParams.set(\"subjectId\", subjectId);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/intelligence/reports\"\nquery = {}\n_subjectType = os.environ.get(\"SANDBOX_SUBJECT_TYPE\")\nif _subjectType is not None: query[\"subjectType\"] = _subjectType\n_subjectId = os.environ.get(\"SANDBOX_SUBJECT_ID\")\nif _subjectId is not None: query[\"subjectId\"] = _subjectId\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/intelligence/reports\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_SUBJECT_TYPE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}subjectType=${SANDBOX_SUBJECT_TYPE}\"\nfi\nif [ -n \"${SANDBOX_SUBJECT_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}subjectId=${SANDBOX_SUBJECT_ID}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/intelligence/reports/estimate":{"post":{"tags":["Intelligence"],"summary":"Estimate an intelligence report","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string","minLength":1,"maxLength":256},"dispatchId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"}},"required":["type","id"]},"mode":{"type":"string","enum":["deterministic","agentic"],"default":"deterministic"},"metadata":{"type":"object","additionalProperties":{}}},"required":["subject"]}}}},"responses":{"200":{"description":"Cost estimate for prospective intelligence report","content":{"application/json":{"schema":{"type":"object","properties":{"estimate":{"type":"object","properties":{"mode":{"type":"string","enum":["deterministic","agentic"]},"costUsd":{"type":"number","minimum":0},"billable":{"type":"boolean"},"billedTo":{"type":"string","enum":["platform","customer"]},"reason":{"type":"string"}},"required":["mode","costUsd","billable","billedTo","reason"]}},"required":["estimate"]}}}},"404":{"description":"Subject not found or not owned by caller"}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/intelligence/reports/estimate`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/intelligence/reports/estimate`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/intelligence/reports/estimate\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/intelligence/reports/estimate\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/intelligence/reports/{jobId}":{"get":{"tags":["Intelligence"],"summary":"Get an intelligence report","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128},"required":true,"name":"jobId","in":"path"}],"responses":{"200":{"description":"Intelligence report","content":{"application/json":{"schema":{"type":"object","properties":{"report":{"type":"object","properties":{"jobId":{"type":"string"},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox","fleet"]},"id":{"type":"string"},"dispatchId":{"type":"string"}},"required":["type","id"]},"mode":{"type":"string","enum":["deterministic","agentic"],"default":"deterministic"},"status":{"type":"string","enum":["queued","running","completed","failed"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean"},"billedTo":{"type":"string","enum":["platform","customer"]},"costUsd":{"type":"number"},"reason":{"type":"string"},"budgetMaxUsd":{"type":"number"}},"required":["billable","billedTo","costUsd","reason"]},"result":{"type":["object","null"],"additionalProperties":{}},"error":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"completedAt":{"type":"string"}},"required":["jobId","subject","status","billing","result","createdAt","updatedAt"]}},"required":["report"]}}}},"404":{"description":"Not found"}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/intelligence/reports/${encodeURIComponent(jobId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/intelligence/reports/${encodeURIComponent(jobId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_jobId = os.environ.get(\"SANDBOX_JOB_ID\")\nif not _jobId: raise SystemExit(\"Set SANDBOX_JOB_ID\")\nurl = f\"{base_url}/v1/intelligence/reports/{quote(_jobId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/intelligence/reports/${SANDBOX_JOB_ID:?Set SANDBOX_JOB_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines":{"get":{"summary":"List lines","description":"A sandbox key lists only lines attached to its own sandbox. A workspace key lists only its workspace's lines. `profileId` keeps lines attached with that profile and unattached lines whose `clientReference` starts with `profile:<profileId>:`.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"sandboxId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"profileId","in":"query"}],"responses":{"200":{"description":"The caller's lines","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"lines":{"type":"array","items":{"$ref":"#/components/schemas/Line"}}},"required":["lines"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"This key cannot list lines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/lines`, baseUrl);\nconst sandboxId = process.env.SANDBOX_SANDBOX_ID;\nif (sandboxId !== undefined) url.searchParams.set(\"sandboxId\", sandboxId);\nconst profileId = process.env.SANDBOX_PROFILE_ID;\nif (profileId !== undefined) url.searchParams.set(\"profileId\", profileId);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines`, baseUrl);\nconst sandboxId = process.env.SANDBOX_SANDBOX_ID;\nif (sandboxId !== undefined) url.searchParams.set(\"sandboxId\", sandboxId);\nconst profileId = process.env.SANDBOX_PROFILE_ID;\nif (profileId !== undefined) url.searchParams.set(\"profileId\", profileId);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines\"\nquery = {}\n_sandboxId = os.environ.get(\"SANDBOX_SANDBOX_ID\")\nif _sandboxId is not None: query[\"sandboxId\"] = _sandboxId\n_profileId = os.environ.get(\"SANDBOX_PROFILE_ID\")\nif _profileId is not None: query[\"profileId\"] = _profileId\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_SANDBOX_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}sandboxId=${SANDBOX_SANDBOX_ID}\"\nfi\nif [ -n \"${SANDBOX_PROFILE_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}profileId=${SANDBOX_PROFILE_ID}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"summary":"Create a line from a connection","description":"The connection must be active and support the transport. A repeated `clientReference` returns the existing line. A 409 that carries `error.details.retryAfterSeconds` can be retried after that many seconds.","tags":["Lines"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["telegram"]}},"required":["connectionId","transport"],"additionalProperties":false},{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["imessage"]},"phoneNumberId":{"type":"string","minLength":1,"maxLength":256,"description":"Required for a numbered provider"}},"required":["connectionId","transport"],"additionalProperties":false},{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["email"]},"address":{"type":"string","maxLength":320,"format":"email"}},"required":["connectionId","transport"],"additionalProperties":false},{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["sms"]},"phoneNumberId":{"type":"string","minLength":1,"maxLength":256}},"required":["connectionId","transport","phoneNumberId"],"additionalProperties":false},{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["whatsapp"]},"phoneNumberId":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[\\x21-\\x7e]+$"}},"required":["connectionId","transport","phoneNumberId"],"additionalProperties":false},{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"label":{"type":"string","minLength":1,"maxLength":80},"clientReference":{"type":"string","minLength":1,"maxLength":120,"description":"Idempotency reference: the same value returns the same line"},"transport":{"type":"string","enum":["voice"]},"phoneNumberId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}},"required":["connectionId","transport","phoneNumberId"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"The line, new or existing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"line":{"$ref":"#/components/schemas/Line"}},"required":["line"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, or the connection does not support the transport","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The connection is not found (`HUB_CONNECTION_MISSING`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The connection is not active, or the reference, address or workspace already names another line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"422":{"description":"The provider refused the line (`HUB_PROVIDER_REJECTED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"500":{"description":"The connection could not be rechecked after a WhatsApp scope change (`HUB_EXECUTOR_FAILURE`); read the line, then retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment, or the provider failed (`HUB_PROVIDER_FAILURE`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/context":{"get":{"summary":"Get the current turn's line context","description":"Send the line token a member's sandbox receives for its current turn. Member addresses are never returned.","tags":["Lines"],"responses":{"200":{"description":"The turn's member, line, directory, capabilities and quotas","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"self":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"role":{"type":"string"}},"required":["id","label","role"]},"line":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"transport":{"type":"string","enum":["imessage","sms","whatsapp","email","voice","telegram"]}},"required":["id","label","transport"]},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"role":{"type":"string"},"self":{"type":"boolean"},"consent":{"type":"string","enum":["enabled","required"]}},"required":["id","label","role","self","consent"]}},"capabilities":{"type":"object","properties":{"selfMessage":{"type":"object","properties":{"allowed":{"type":"boolean"},"reason":{"type":"string"}},"required":["allowed"]},"memberRelay":{"type":"object","properties":{"allowed":{"type":"boolean"},"reason":{"type":"string"}},"required":["allowed"]},"reminder":{"type":"object","properties":{"allowed":{"type":"boolean"},"reason":{"type":"string"}},"required":["allowed"]}},"required":["selfMessage","memberRelay","reminder"]},"pendingReminders":{"type":"array","items":{"type":"object","properties":{"messageId":{"type":"string"},"wakeAt":{"anyOf":[{"type":"string"},{"type":"number"}]},"prompt":{"type":["string","null"]}},"required":["messageId","prompt"]}},"quotas":{"type":"object","properties":{"selfMessagesToday":{"type":"integer"},"selfMessagesPerDay":{"type":"integer"},"selfMessagesPending":{"type":"integer"},"selfMessagesPendingLimit":{"type":"integer"}},"required":["selfMessagesToday","selfMessagesPerDay","selfMessagesPending","selfMessagesPendingLimit"]}},"required":["self","line","members","capabilities","pendingReminders","quotas"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"No current turn token was sent, or its turn has ended (`HUB_RELAY_DENIED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The token's sandbox no longer serves this line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/lines/context`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines/context`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines/context\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/context\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/execution-keys":{"get":{"summary":"List execution keys","description":"Account owner only. Lists the account's active root Sandbox keys that can run an attached line. No key secret is returned.","tags":["Lines"],"responses":{"200":{"description":"Keys that can run a line","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"expiresAt":{"type":["string","null"]}},"required":["id","name","expiresAt"]}}},"required":["keys"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/lines/execution-keys`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines/execution-keys`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines/execution-keys\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/execution-keys\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/numbers":{"get":{"summary":"List a connection's phone numbers","description":"Account owner only. Reading inventory never reserves or buys a number. `exhaustive: false` means the provider returned one page, so an owned number can be missing; create the line with its exact ID.","tags":["Lines"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256},"required":true,"name":"connectionId","in":"query"},{"schema":{"type":"string","enum":["imessage","sms","whatsapp"]},"required":true,"name":"transport","in":"query"}],"responses":{"200":{"description":"Numbers the connection owns","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"providerId":{"type":"string"},"transport":{"type":"string","enum":["imessage","sms","whatsapp"]},"selection":{"type":"string","enum":["identity","number"]},"numbers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"address":{"type":"string"}},"required":["id","address"]}},"exhaustive":{"type":"boolean"},"reserved":{"type":"boolean","enum":[false]}},"required":["providerId","transport","selection","numbers","exhaustive","reserved"]}},"required":["success","data"]}}}},"400":{"description":"`connectionId` or a phone `transport` is missing, or the provider does not support the transport","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The connection is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The connection is not active, changed during the lookup, or needs its webhook first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or number discovery are not configured on this deployment, or the provider failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/lines/numbers`, baseUrl);\nconst connectionId = process.env.SANDBOX_CONNECTION_ID;\nif (!connectionId) throw new Error(\"Set SANDBOX_CONNECTION_ID\");\nif (connectionId !== undefined) url.searchParams.set(\"connectionId\", connectionId);\nconst transport = process.env.SANDBOX_TRANSPORT;\nif (!transport) throw new Error(\"Set SANDBOX_TRANSPORT\");\nif (transport !== undefined) url.searchParams.set(\"transport\", transport);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines/numbers`, baseUrl);\nconst connectionId = process.env.SANDBOX_CONNECTION_ID;\nif (!connectionId) throw new Error(\"Set SANDBOX_CONNECTION_ID\");\nif (connectionId !== undefined) url.searchParams.set(\"connectionId\", connectionId);\nconst transport = process.env.SANDBOX_TRANSPORT;\nif (!transport) throw new Error(\"Set SANDBOX_TRANSPORT\");\nif (transport !== undefined) url.searchParams.set(\"transport\", transport);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines/numbers\"\nquery = {}\n_connectionId = os.environ.get(\"SANDBOX_CONNECTION_ID\")\nif not _connectionId: raise SystemExit(\"Set SANDBOX_CONNECTION_ID\")\nif _connectionId is not None: query[\"connectionId\"] = _connectionId\n_transport = os.environ.get(\"SANDBOX_TRANSPORT\")\nif not _transport: raise SystemExit(\"Set SANDBOX_TRANSPORT\")\nif _transport is not None: query[\"transport\"] = _transport\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/numbers\"\n# URL-encode each query value before exporting the parameter.\n: \"${SANDBOX_CONNECTION_ID:?Set SANDBOX_CONNECTION_ID}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}connectionId=${SANDBOX_CONNECTION_ID}\"\n: \"${SANDBOX_TRANSPORT:?Set SANDBOX_TRANSPORT}\"\nseparator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}transport=${SANDBOX_TRANSPORT}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/providers":{"get":{"summary":"List line providers","description":"Account owner only: send the account's root API key.","tags":["Lines"],"responses":{"200":{"description":"Provider and transport pairs this deployment binds","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"providerId":{"type":"string"},"transport":{"type":"string","enum":["imessage","sms","whatsapp","email","voice","telegram"]},"selection":{"type":"string","enum":["number","identity","mailbox"]}},"required":["providerId","transport","selection"]}}},"required":["providers"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/lines/providers`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/lines/providers`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/lines/providers\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/providers\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}":{"get":{"summary":"Get a line","description":"A sandbox key reads only a line attached to its own sandbox. A released line answers 404.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"responses":{"200":{"description":"The line","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"line":{"$ref":"#/components/schemas/Line"}},"required":["line"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"This key cannot read lines","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"summary":"Release a line","description":"Detach the line and turn its voice off first. `released` is false when the line was already released.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"responses":{"200":{"description":"The release result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"released":{"type":"boolean"}},"required":["released"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The line is attached, has voice on, or belongs to a managed number order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"patch":{"summary":"Turn voice on or off","description":"On a metered or role-policy line, only the account's root key can change voice.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"voice":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean","enum":[true]},"ph0ny":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1,"maxLength":256},"agentId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"phoneNumberId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}},"required":["connectionId","agentId"],"additionalProperties":false}},"required":["enabled","ph0ny"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","enum":[false]}},"required":["enabled"],"additionalProperties":false}]}},"required":["voice"],"additionalProperties":false}}}},"responses":{"200":{"description":"The line with its voice state","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"line":{"$ref":"#/components/schemas/Line"}},"required":["line"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key can change a line, and only the account's root key on a metered or role-policy line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or its phone connection is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The line is not active or is application-routed, or its connection is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"422":{"description":"The voice provider refused (`HUB_PROVIDER_REJECTED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or voice are not configured on this deployment, or the voice provider failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/attachment":{"put":{"summary":"Attach a line","description":"Pass one execution target: `sandboxId`, `instance`, `placement: \"worker\"`, a shared line with `respond.application`, or `respond.kind: \"none\"`. Declare at least one member, unless a shared Telegram line installs its own. The calling API key becomes the execution key. Repeating an identical attach returns the existing attachment with 200. An allowance meter needs a plan on that meter before attaching.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"runAsKeyId":{"type":"string","minLength":1,"maxLength":256,"description":"Execution key ID; an API key may name only itself"},"senderVerificationId":{"type":"string","pattern":"^lsv_[A-Za-z0-9_-]+$","description":"A verified sender test, consumed on attach"},"sandboxId":{"type":"string","minLength":1,"maxLength":256,"description":"The sandbox every member runs in"},"instance":{"type":"object","properties":{"keyPrefix":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:@-]{0,99}$","description":"Each member's instance key is this prefix plus 32 hex digits"},"create":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":63},"environment":{"type":"string","minLength":1,"maxLength":128},"ownerContext":{"type":"string","enum":["isolated"]},"capabilities":{"type":"array","items":{"type":"string","enum":["computer_use"]},"maxItems":1},"resources":{"type":"object","properties":{"cpuCores":{"type":"number","exclusiveMinimum":0,"maximum":64},"memoryMB":{"type":"integer","exclusiveMinimum":0,"maximum":262144},"diskGB":{"type":"integer","exclusiveMinimum":0,"maximum":2048}},"additionalProperties":false},"egressPolicy":{"type":"object","additionalProperties":{},"description":"A sandbox egress policy"},"idleTimeoutSeconds":{"type":"integer","minimum":60,"maximum":7776000},"maxLifetimeSeconds":{"type":"integer","minimum":60,"maximum":7776000},"deleteAfterStoppedSeconds":{"type":"integer","minimum":60,"maximum":7776000},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":256},"description":"Up to 16 fields; keys up to 64 characters"}},"default":{},"additionalProperties":false}},"required":["keyPrefix"],"additionalProperties":false,"description":"Run each member in their own named instance instead"},"placement":{"type":"string","enum":["worker"],"description":"Run turns without a sandbox, with the profile and `connections` only"},"connections":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"maxItems":20,"description":"Connection IDs a worker turn may call"},"mode":{"type":"string","enum":["personal","shared"],"description":"`personal`: one owner member. `shared`: one thread each"},"payer":{"type":"string","enum":["owner","member"],"default":"owner"},"members":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string","minLength":3,"maxLength":320,"description":"E.164 number or email address the member sends from"},"role":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,31}$"},"label":{"type":"string","minLength":1,"maxLength":60},"subjectId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$","description":"Owner-authored customer identity, never a phone number"},"allowance":{"type":"object","properties":{"member":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"payer":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"role":{"type":"string","enum":["owner","member"]},"payerRole":{"type":"string","enum":["owner","member"],"description":"Needs `payer`"},"paywallNotice":{"type":"string","minLength":1,"maxLength":480}},"required":["member"],"additionalProperties":false},"voiceConsent":{"type":"object","properties":{"recordedAt":{"type":"string","format":"date-time"},"sourceRef":{"type":"string","minLength":1,"maxLength":512}},"required":["recordedAt","sourceRef"],"additionalProperties":false,"description":"Owner attestation of the member's consent; voice lines"}},"required":["address","role"],"additionalProperties":false},"maxItems":50},"unknownSenders":{"type":"string","enum":["reject","guest"],"default":"reject"},"roles":{"type":"object","additionalProperties":{"type":"object","properties":{"context":{"type":"string","enum":["own","shared"],"description":"`shared` turns see other shared members' recent exchanges"},"tools":{"type":"string","enum":["act","chat"],"description":"`chat` sessions get no shell, files, subagents or MCP servers"}},"required":["context","tools"],"additionalProperties":false},"description":"Keyed by role name: a lowercase word of up to 32 characters"},"respond":{"type":"object","properties":{"kind":{"type":"string","enum":["agent","none"],"description":"`none` answers asks, coded replies and notices only, and runs no agent"},"application":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"pattern":"^https:\\/\\//i"},"secret":{"type":"string","minLength":16,"maxLength":512,"pattern":"^[\\x21-\\x7e]+$","description":"Bearer credential the application checks; never returned"},"binding":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^[\\x21-\\x7e]+$"}},"required":["url","secret","binding"],"additionalProperties":false},"profileId":{"type":"string","minLength":1,"maxLength":256,"description":"Stored agent profile; not with `backend.profile`"},"backend":{"type":"object","additionalProperties":{},"description":"Agent backend configuration for member sessions"},"rolePolicy":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"roles":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["general"]}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["scoped"]},"mcpServers":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z][A-Za-z0-9_-]{0,63}$"},"minItems":1,"maxItems":16}},"required":["kind","mcpServers"],"additionalProperties":false}]},"description":"Role name to its agent scope"},"members":{"type":"object","additionalProperties":{"type":"object","properties":{"backend":{"type":"object","additionalProperties":{},"description":"Agent backend configuration for member sessions"}},"required":["backend"],"additionalProperties":false},"description":"Member address to its own backend; up to 50"}},"required":["version","roles","members"],"additionalProperties":false},"telegram":{"type":"object","properties":{"mode":{"type":"string","enum":["moderation","mentions"]},"deleteClearSpam":{"type":"boolean","default":false},"turnsPerGroupPerHour":{"type":"integer","minimum":1,"maximum":1000,"default":120},"allowedChatIds":{"type":"array","items":{"type":"string","pattern":"^-[1-9]\\d{0,15}$"},"minItems":1,"maxItems":100}},"required":["mode"],"additionalProperties":false},"gate":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"pattern":"^https:\\/\\//i"},"context":{"type":"object","additionalProperties":{"type":"string","maxLength":256},"description":"Up to 8 fixed fields sent with every candidate"},"audience":{"type":"string","enum":["owner","members"],"default":"members"},"secret":{"type":"string","minLength":16,"maxLength":512}},"required":["url","secret"],"additionalProperties":false,"description":"HTTPS endpoint that checks each agent answer before it is sent"},"acknowledgement":{"type":"boolean","default":true},"readReceipts":{"type":"boolean","default":true}},"default":{"kind":"agent","acknowledgement":true,"readReceipts":true},"required":["kind"],"additionalProperties":false},"limits":{"type":"object","properties":{"turnsPerMemberPerDay":{"type":"integer","minimum":1,"maximum":10000},"noticesPerSenderPerDay":{"type":"integer","minimum":0,"maximum":100},"noticesPerLinePerDay":{"type":"integer","minimum":0,"maximum":10000},"allowance":{"type":"object","properties":{"member":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"payer":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"role":{"type":"string","enum":["owner","member"]},"payerRole":{"type":"string","enum":["owner","member"],"description":"Needs `payer`"},"paywallNotice":{"type":"string","minLength":1,"maxLength":480},"meter":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"perMember":{"type":"boolean"}},"required":["meter"],"additionalProperties":false}},"additionalProperties":false},"clientReference":{"type":"string","minLength":1,"maxLength":120}},"required":["mode","members"],"additionalProperties":false}}}},"responses":{"200":{"description":"The existing attachment","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"attachment":{"allOf":[{"$ref":"#/components/schemas/LineAttachment"},{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/LineMember"}}},"required":["members"]}]}},"required":["attachment"]}},"required":["success","data"]}}}},"201":{"description":"The new attachment","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"attachment":{"allOf":[{"$ref":"#/components/schemas/LineAttachment"},{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/LineMember"}}},"required":["members"]}]}},"required":["attachment"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, a role is unknown, or the allowance meter has no plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"This key cannot attach the line, run the sandbox, name another execution key, or set a role policy or allowance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line, sandbox or stored profile is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The line is not active, was attached concurrently, or is attached differently; detach first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"422":{"description":"The line cannot verify senders (`HUB_SENDER_VERIFICATION_UNSUPPORTED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or a required service are not configured on this deployment, or that service failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/attachment\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/attachment\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"summary":"Detach a line","description":"Detaches every attachment the line holds, or with `If-Match: \"lat_…\"` only that attachment. To detach only an attachment you observed, use `DELETE /v1/lines/{lineId}/attachments/{attachmentId}`.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"responses":{"200":{"description":"`detached` is false when nothing was attached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"detached":{"type":"boolean"}},"required":["detached"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A metered or role-policy line needs the account's root key or the line's workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"An application callback is still running; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/attachment\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/attachment\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/attachment/reauthorize":{"put":{"summary":"Change an attachment's execution key","description":"Send the request with the replacement API key, which becomes the execution key. Both expected IDs must match the attachment as last read.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedAttachmentId":{"type":"string","pattern":"^lat_[A-Za-z0-9_-]+$"},"expectedRunAsKeyId":{"type":"string","pattern":"^key_[A-Za-z0-9_-]+$"}},"required":["expectedAttachmentId","expectedRunAsKeyId"],"additionalProperties":false}}}},"responses":{"200":{"description":"The attachment with its new execution key","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"attachment":{"$ref":"#/components/schemas/LineAttachment"}},"required":["attachment"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an API key can reauthorize, it cannot run the sandbox, or the line needs the account's root key or its workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or sandbox is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The expected attachment or execution key no longer matches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or the sandbox service are not configured on this deployment, or it failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment/reauthorize`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachment/reauthorize`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/attachment/reauthorize\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/attachment/reauthorize\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/attachments/{attachmentId}":{"delete":{"summary":"Detach one attachment","description":"Detaches only this attachment, so a retry never detaches its replacement.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"attachmentId","in":"path"}],"responses":{"200":{"description":"`detached` is false when it was already detached","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"detached":{"type":"boolean"}},"required":["detached"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A metered or role-policy line needs the account's root key or the line's workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or attachment is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"An application callback is still running; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst attachmentId = process.env.SANDBOX_ATTACHMENT_ID;\nif (!attachmentId) throw new Error(\"Set SANDBOX_ATTACHMENT_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachments/${encodeURIComponent(attachmentId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst attachmentId = process.env.SANDBOX_ATTACHMENT_ID;\nif (!attachmentId) throw new Error(\"Set SANDBOX_ATTACHMENT_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/attachments/${encodeURIComponent(attachmentId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_attachmentId = os.environ.get(\"SANDBOX_ATTACHMENT_ID\")\nif not _attachmentId: raise SystemExit(\"Set SANDBOX_ATTACHMENT_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/attachments/{quote(_attachmentId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/attachments/${SANDBOX_ATTACHMENT_ID:?Set SANDBOX_ATTACHMENT_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/captures":{"get":{"summary":"List owner captures","description":"Lists the texts the line's declared owner sent as `idea <text>`, oldest first. Pass the last page's `next` as `after` to resume.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"after","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"A page of captures","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"captures":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"lineId":{"type":"string"},"keyword":{"type":"string","enum":["idea"]},"text":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","lineId","keyword","text","createdAt"]}},"hasMore":{"type":"boolean"},"next":{"type":"string","description":"Cursor for `after`; absent on an empty page"}},"required":["captures","hasMore"]}},"required":["success","data"]}}}},"400":{"description":"`after` is not a cursor from this route, or `limit` is not an integer from 1 to 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/captures`, baseUrl);\nconst after = process.env.SANDBOX_AFTER;\nif (after !== undefined) url.searchParams.set(\"after\", after);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/captures`, baseUrl);\nconst after = process.env.SANDBOX_AFTER;\nif (after !== undefined) url.searchParams.set(\"after\", after);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/captures\"\nquery = {}\n_after = os.environ.get(\"SANDBOX_AFTER\")\nif _after is not None: query[\"after\"] = _after\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/captures\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_AFTER:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}after=${SANDBOX_AFTER}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/members":{"get":{"summary":"List line members","description":"An API key gets the members of the line's current attachment. A turn's line token gets the member directory, which has no addresses.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"responses":{"200":{"description":"The members or the turn's directory","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"anyOf":[{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/LineMember"}}},"required":["members"],"title":"Members"},{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"role":{"type":"string"},"self":{"type":"boolean"},"consent":{"type":"string","enum":["enabled","required"]}},"required":["id","label","role","self","consent"]}}},"required":["members"],"title":"Turn directory"}]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key cannot read members, or the turn token has ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"summary":"Add a line member","description":"Adding a member that exists with the same binding returns that member. A personal line has exactly one member.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string","minLength":3,"maxLength":320,"description":"E.164 number or email address the member sends from"},"role":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,31}$"},"label":{"type":"string","minLength":1,"maxLength":60},"subjectId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$","description":"Owner-authored customer identity, never a phone number"},"allowance":{"type":"object","properties":{"member":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"payer":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"role":{"type":"string","enum":["owner","member"]},"payerRole":{"type":"string","enum":["owner","member"],"description":"Needs `payer`"},"paywallNotice":{"type":"string","minLength":1,"maxLength":480}},"required":["member"],"additionalProperties":false},"voiceConsent":{"type":"object","properties":{"recordedAt":{"type":"string","format":"date-time"},"sourceRef":{"type":"string","minLength":1,"maxLength":512}},"required":["recordedAt","sourceRef"],"additionalProperties":false,"description":"Owner attestation of the member's consent; voice lines"}},"required":["address","role"],"additionalProperties":false}}}},"responses":{"201":{"description":"The member, new or existing","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/LineMember"}},"required":["member"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, the role is unknown, the line is personal, or the address does not fit the transport","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A metered or role-policy line needs the account's root key or the line's workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The address or `subjectId` already belongs to another member binding","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/members/{memberId}":{"patch":{"summary":"Update a line member","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","pattern":"^[a-z][a-z0-9_-]{0,31}$"},"label":{"type":"string","minLength":1,"maxLength":60}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated member","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/LineMember"}},"required":["member"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, the role is unknown, or the role does not fit the line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A metered or role-policy line needs the account's root key or the line's workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line, attachment or member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"An application callback for the member is still running, or a shared application line's owner changes only with a newly verified sender","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"summary":"Remove a line member","description":"A personal line's member and a role-policy line's last owner cannot be removed; detach the line instead.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"The member is removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"removed":{"type":"boolean","enum":[true]}},"required":["removed"]}},"required":["success","data"]}}}},"400":{"description":"This member cannot be removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A metered or role-policy line needs the account's root key or the line's workspace key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line, attachment or member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"An application callback for the member is still running, or a shared application line's owner changes only with a newly verified sender","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/members/{memberId}/application":{"get":{"summary":"Get a member's application","description":"Account owner only. Pass `memberRevision` as `expectedMemberRevision` when changing the selection.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"The member's revision and selection","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"memberRevision":{"type":"string"},"selection":{"type":["object","null"],"properties":{"id":{"type":"string"},"lineId":{"type":"string"},"attachmentId":{"type":"string"},"memberId":{"type":"string"},"subjectId":{"type":"string"},"status":{"type":"string","enum":["active","revoked"]},"application":{"type":"object","properties":{"url":{"type":"string"},"binding":{"type":"string"}},"required":["url","binding"]},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"app":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}},"required":["id","lineId","attachmentId","memberId","subjectId","status","application","createdAt","revokedAt","app"]}},"required":["memberRevision","selection"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or active member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}/application\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}/application\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"put":{"summary":"Select a member's application","description":"Account owner only. The change applies only while `expectedSelectionId` and `expectedMemberRevision` match. Bindings that start with `line-grant:` are reserved.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedSelectionId":{"type":["string","null"],"minLength":1,"maxLength":128},"expectedMemberRevision":{"type":"string","minLength":1,"maxLength":64},"subjectId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9:._-]{0,127}$"},"application":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"pattern":"^https:\\/\\//i"},"secret":{"type":"string","minLength":16,"maxLength":512,"pattern":"^[\\x21-\\x7e]+$","description":"Bearer credential the application checks; never returned"},"binding":{"type":"string","minLength":1,"maxLength":1024,"pattern":"^[\\x21-\\x7e]+$"}},"required":["url","secret","binding"],"additionalProperties":false}},"required":["expectedSelectionId","expectedMemberRevision","subjectId","application"],"additionalProperties":false}}}},"responses":{"200":{"description":"The new selection","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"selection":{"type":"object","properties":{"id":{"type":"string"},"lineId":{"type":"string"},"attachmentId":{"type":"string"},"memberId":{"type":"string"},"subjectId":{"type":"string"},"status":{"type":"string","enum":["active","revoked"]},"application":{"type":"object","properties":{"url":{"type":"string"},"binding":{"type":"string"}},"required":["url","binding"]},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","lineId","attachmentId","memberId","subjectId","status","application","createdAt","revokedAt"]}},"required":["selection"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, the URL is not public HTTPS, or the binding is reserved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can select, or the member cannot use that application","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line, active attachment or member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The selection or member revision changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or shared applications are not enabled on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}/application\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}/application\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"summary":"Clear a member's application","description":"Account owner only. The change applies only while both expected values match.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"expectedSelectionId":{"type":"string","minLength":1,"maxLength":128},"expectedMemberRevision":{"type":"string","minLength":1,"maxLength":64}},"required":["expectedSelectionId","expectedMemberRevision"],"additionalProperties":false}}}},"responses":{"200":{"description":"The selection is cleared","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"selection":{"type":"null"}},"required":["selection"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or active member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The selection or member revision changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/application`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}/application\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}/application\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/members/{memberId}/reminders":{"get":{"summary":"Get a member's reminder preferences","description":"`enabled` is true once the member texts `REMINDERS ON` with a timezone on the current attachment.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"The member's reminder preferences","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"enabled":{"type":"boolean"},"timezone":{"type":["string","null"]},"quietStart":{"type":["string","null"]},"quietEnd":{"type":["string","null"]},"consentMessageId":{"type":["string","null"]},"updatedAt":{"type":["string","null"]}},"required":["enabled","timezone","quietStart","quietEnd","consentMessageId","updatedAt"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or the line outbox are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/reminders`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/members/${encodeURIComponent(memberId)}/reminders`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/members/{quote(_memberId or '', safe='')}/reminders\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}/reminders\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/messages/{messageId}":{"get":{"summary":"Get a line message","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"messageId","in":"path"}],"responses":{"200":{"description":"The message","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"message":{"$ref":"#/components/schemas/LineMessage"}},"required":["message"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or message is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/messages/${encodeURIComponent(messageId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/messages/${encodeURIComponent(messageId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_messageId = os.environ.get(\"SANDBOX_MESSAGE_ID\")\nif not _messageId: raise SystemExit(\"Set SANDBOX_MESSAGE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/messages/{quote(_messageId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/messages/${SANDBOX_MESSAGE_ID:?Set SANDBOX_MESSAGE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/messages/{messageId}/application-dispatch/current":{"post":{"summary":"Check an application dispatch","description":"Send the line application's callback secret as the `Bearer` token, not an API key. `current: false` means the dispatch is stale and must not be admitted.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"messageId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attachmentId":{"type":"string","minLength":1,"maxLength":128},"memberId":{"type":"string","minLength":1,"maxLength":128},"memberRevision":{"type":"string","minLength":1,"maxLength":64},"senderAddress":{"type":"string","minLength":3,"maxLength":320},"textSha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"binding":{"type":"string","minLength":1,"maxLength":256},"dispatchFence":{"type":"string","pattern":"^a[1-9]\\d*\\.[1-9]\\d*$"},"dispatchDeadline":{"type":"string","minLength":24,"maxLength":24}},"required":["attachmentId","memberId","memberRevision","senderAddress","textSha256","binding","dispatchFence","dispatchDeadline"],"additionalProperties":false}}}},"responses":{"200":{"description":"Whether the dispatch may still be admitted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"current":{"type":"boolean"}},"required":["current"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"The callback secret is missing or wrong, or the application binding is inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The body is larger than 4 KiB (`HUB_INVALID_INPUT`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/messages/${encodeURIComponent(messageId)}/application-dispatch/current`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/messages/${encodeURIComponent(messageId)}/application-dispatch/current`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_messageId = os.environ.get(\"SANDBOX_MESSAGE_ID\")\nif not _messageId: raise SystemExit(\"Set SANDBOX_MESSAGE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/messages/{quote(_messageId or '', safe='')}/application-dispatch/current\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/messages/${SANDBOX_MESSAGE_ID:?Set SANDBOX_MESSAGE_ID}/application-dispatch/current\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/outbox/{messageId}":{"get":{"summary":"Get a send receipt","description":"`reply.answer` holds the first coded answer once the member replies.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"messageId","in":"path"}],"responses":{"200":{"description":"The send's receipt","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LineSendReceipt"}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or send is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or the line outbox are not configured on this deployment, or the send's record is incomplete (`HUB_OUTBOX_MISSING`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/outbox/${encodeURIComponent(messageId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst messageId = process.env.SANDBOX_MESSAGE_ID;\nif (!messageId) throw new Error(\"Set SANDBOX_MESSAGE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/outbox/${encodeURIComponent(messageId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_messageId = os.environ.get(\"SANDBOX_MESSAGE_ID\")\nif not _messageId: raise SystemExit(\"Set SANDBOX_MESSAGE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/outbox/{quote(_messageId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/outbox/${SANDBOX_MESSAGE_ID:?Set SANDBOX_MESSAGE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/remind":{"post":{"summary":"Set a reminder for the current turn","description":"Needs the line token of an owner's current turn, and the owner must have reminders on. The member's agent wakes at `notBefore` in the same session and writes the reminder.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":4000},"notBefore":{"type":"string","format":"date-time","description":"A future time"},"idempotencyKey":{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,160}$"}},"required":["text","notBefore","idempotencyKey"],"additionalProperties":false}}}},"responses":{"202":{"description":"The queued reminder","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"state":{"type":"string","enum":["queued"]},"lineId":{"type":"string"},"messageId":{"type":"string"},"sessionId":{"type":"string"},"workflowRunId":{"type":"string"},"completion":{"type":"string","enum":["read_line_receipt"],"description":"Queued work, not completed work: read the line receipt"}},"required":["state","lineId","messageId","sessionId","workflowRunId","completion"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, or `notBefore` is not in the future","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"No owner turn token was sent, or its turn has ended (`HUB_RELAY_DENIED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"Reminders are off, the key names another request, or the line's reply gate forbids it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/remind`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/remind`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/remind\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/remind\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/send":{"post":{"summary":"Send a message to a member","description":"Queues a proactive message; the receipt is not proof of delivery. The member must have texted `REMINDERS ON` with a timezone. Repeating an `idempotencyKey` returns the first send, and the same key with another request answers 409 `HUB_IDEMPOTENCY_CONFLICT`. An owner API key sends the owner body; a turn's line token sends the turn body.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"memberId":{"type":"string","minLength":1,"maxLength":256},"text":{"type":"string","minLength":1,"maxLength":4000,"description":"Up to 1,024 characters when `image` is set"},"idempotencyKey":{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,160}$","description":"Bound to the whole request, `reply` included"},"notBefore":{"type":"string","format":"date-time","description":"Less than seven days away"},"image":{"type":"object","properties":{"url":{"type":"string","maxLength":2048}},"required":["url"],"additionalProperties":false,"description":"Public HTTPS URL without credentials, query or fragment; native Meta WhatsApp lines only"},"reply":{"type":"object","properties":{"code":{"type":"string","pattern":"^(?=[A-Za-z0-9]*\\d)[A-Za-z][A-Za-z0-9]{0,7}$","description":"Compared case-insensitively","example":"F7"},"ttlSeconds":{"type":"integer","minimum":60,"maximum":604800},"options":{"type":"array","items":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$"},"minItems":1,"maxItems":20,"description":"Distinct ignoring case"}},"required":["code","ttlSeconds"],"additionalProperties":false,"description":"Ask a coded question; a reply that starts with `code` returns to this send"}},"required":["memberId","text","idempotencyKey"],"additionalProperties":false,"title":"Owner send"},{"type":"object","properties":{"toMember":{"type":"string","minLength":1,"maxLength":128,"description":"Member ID, member label, or `self`"},"text":{"type":"string","minLength":1,"maxLength":4000},"notBefore":{"type":"string","format":"date-time"},"onBehalfOf":{"type":"string","minLength":1,"maxLength":128,"description":"The turn's sender, by ID or label; required to relay"},"idempotencyKey":{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,160}$"}},"required":["toMember","text"],"additionalProperties":false,"title":"Turn send"}]}}}},"responses":{"202":{"description":"The queued send","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/LineSendReceipt"}},"required":["success","data"]}}}},"400":{"description":"The body is invalid, or `notBefore` is too far away","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key or a current turn's line token can send, and the turn's role may not relay","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line, attachment or member is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The member has not opted in, is not active, or has no retained conversation, or the key, code, image or reply gate conflicts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests, or a recipient or line send limit is reached (`HUB_OUTBOX_LIMIT`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines or the line outbox are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/send`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/send`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/send\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/send\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/sender-verifications":{"post":{"summary":"Start a sender verification","description":"Needs an owner API key, and the line must be unattached. Text `testText` to the line from the handset to enroll before `expiresAt`, then answer the private code the line sends back. A new test replaces the open one. Attach with the verified `testId` as `senderVerificationId`.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"201":{"description":"The test to send","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"lineId":{"type":"string"},"testId":{"type":"string"},"state":{"type":"string","enum":["awaiting_test"]},"testText":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["lineId","testId","state","testText","expiresAt"]}},"required":["success","data"]}}}},"400":{"description":"The body is not empty or `{}`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The line is not active (`HUB_LINE_INACTIVE`), is attached, or its connection changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"422":{"description":"The line's provider or transport cannot verify senders (`HUB_SENDER_VERIFICATION_UNSUPPORTED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/sender-verifications`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/sender-verifications`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/sender-verifications\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/sender-verifications\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/sender-verifications/{testId}":{"get":{"summary":"Get a sender verification","description":"Needs the API key that started the test. `approvedSender` is present only in state `verified`; keep it out of browser-visible state.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"testId","in":"path"}],"responses":{"200":{"description":"The test's state and proof times","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"lineId":{"type":"string"},"testId":{"type":"string"},"state":{"type":"string","enum":["awaiting_test","sending","challenge_sent","verified","consumed","cancelled","superseded","failed","expired"]},"expiresAt":{"type":"string","format":"date-time"},"proof":{"type":"object","properties":{"signedInboundTestAt":{"type":["string","null"]},"providerReplyAcknowledgedAt":{"type":["string","null"]},"signedInboundConfirmAt":{"type":["string","null"]}},"required":["signedInboundTestAt","providerReplyAcknowledgedAt","signedInboundConfirmAt"]},"approvedSender":{"type":"string"}},"required":["lineId","testId","state","expiresAt","proof"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only an owner API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or test is not found for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst testId = process.env.SANDBOX_TEST_ID;\nif (!testId) throw new Error(\"Set SANDBOX_TEST_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/sender-verifications/${encodeURIComponent(testId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst testId = process.env.SANDBOX_TEST_ID;\nif (!testId) throw new Error(\"Set SANDBOX_TEST_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/sender-verifications/${encodeURIComponent(testId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_testId = os.environ.get(\"SANDBOX_TEST_ID\")\nif not _testId: raise SystemExit(\"Set SANDBOX_TEST_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/sender-verifications/{quote(_testId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/sender-verifications/${SANDBOX_TEST_ID:?Set SANDBOX_TEST_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/threads":{"get":{"summary":"List line threads","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"}],"responses":{"200":{"description":"Threads, most recent message first","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"threads":{"type":"array","items":{"$ref":"#/components/schemas/LineThread"}}},"required":["threads"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not found or not visible to this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/threads\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/threads\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/lines/{lineId}/threads/{threadId}/approvals":{"get":{"summary":"List pending approvals","description":"Account owner only. Lists the agent actions in the thread's session that wait for the owner's decision.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"threadId","in":"path"}],"responses":{"200":{"description":"The pending requests","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Permission requests waiting in the thread's session, each with its `requestDigest`"}},"required":["requests"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account's root API key can do this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not attached, or the thread has no current approval session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The thread's approval session changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Approvals are not available on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/approvals`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/approvals`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_threadId = os.environ.get(\"SANDBOX_THREAD_ID\")\nif not _threadId: raise SystemExit(\"Set SANDBOX_THREAD_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/threads/{quote(_threadId or '', safe='')}/approvals\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/threads/${SANDBOX_THREAD_ID:?Set SANDBOX_THREAD_ID}/approvals\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"summary":"Decide a pending approval","description":"Only the account owner's signed-in browser session can decide, so every API key gets 403.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"threadId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","deny"]},"requestDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"csrf":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"required":["decision","requestDigest","csrf"]}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","deny"]},"requestDigest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"csrf":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"required":["decision","requestDigest","csrf"]}}}},"responses":{"200":{"description":"The session's acknowledgement","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"result":{"type":"object","properties":{"acknowledgement":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"],"additionalProperties":{}},"serverResult":{}},"required":["acknowledgement"]}},"required":["result"]}},"required":["success","data"]}}}},"400":{"description":"The body is invalid or larger than 4 KiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"Only the account owner's signed-in session can decide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line is not attached, or the thread has no current approval session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The request is no longer awaiting approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"413":{"description":"The request body is larger than 256 KiB (PAYLOAD_TOO_LARGE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Approvals are not available on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/approvals`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/approvals`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_threadId = os.environ.get(\"SANDBOX_THREAD_ID\")\nif not _threadId: raise SystemExit(\"Set SANDBOX_THREAD_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/threads/{quote(_threadId or '', safe='')}/approvals\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/threads/${SANDBOX_THREAD_ID:?Set SANDBOX_THREAD_ID}/approvals\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/lines/{lineId}/threads/{threadId}/messages":{"get":{"summary":"List thread messages","description":"Returns the latest messages, newest first, without paging.","tags":["Lines"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"lineId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"threadId","in":"path"}],"responses":{"200":{"description":"The messages","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/LineMessage"}}},"required":["messages"]}},"required":["success","data"]}}}},"401":{"description":"No `Bearer` token was sent (`LINES_API_KEY_REQUIRED`), or it is not valid (`HUB_UNAUTHENTICATED`)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"403":{"description":"A sandbox key or turn token cannot read this","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"404":{"description":"The line or thread is not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"429":{"description":"Too many requests (`RATE_LIMITED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"502":{"description":"Lines did not answer in time, failed or redirected (`PLATFORM_UNAVAILABLE`); retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinesForwarderError"}}}},"503":{"description":"Lines are not configured on this deployment","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinesForwarderError"},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/messages`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst lineId = process.env.SANDBOX_LINE_ID;\nif (!lineId) throw new Error(\"Set SANDBOX_LINE_ID\");\nconst threadId = process.env.SANDBOX_THREAD_ID;\nif (!threadId) throw new Error(\"Set SANDBOX_THREAD_ID\");\nconst url = new URL(`/v1/lines/${encodeURIComponent(lineId)}/threads/${encodeURIComponent(threadId)}/messages`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_lineId = os.environ.get(\"SANDBOX_LINE_ID\")\nif not _lineId: raise SystemExit(\"Set SANDBOX_LINE_ID\")\n_threadId = os.environ.get(\"SANDBOX_THREAD_ID\")\nif not _threadId: raise SystemExit(\"Set SANDBOX_THREAD_ID\")\nurl = f\"{base_url}/v1/lines/{quote(_lineId or '', safe='')}/threads/{quote(_threadId or '', safe='')}/messages\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/lines/${SANDBOX_LINE_ID:?Set SANDBOX_LINE_ID}/threads/${SANDBOX_THREAD_ID:?Set SANDBOX_THREAD_ID}/messages\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/me":{"get":{"tags":["Account"],"summary":"Get the current account","responses":{"200":{"description":"The caller's account","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"billing_owner_id":{"type":"string","description":"The Platform account that usage is billed to."},"api_key_id":{"type":["string","null"],"description":"The API key that authenticated the request; null when none did."},"billing_delegation_authorized":{"type":"boolean","description":"Whether this credential may choose a funded payer when it creates a sandbox."},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]},"created_at":{"type":"string","format":"date-time"}},"required":["customer_id","billing_owner_id","api_key_id","billing_delegation_authorized","email","tier","created_at"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"404":{"description":"The caller has no customer record (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/me`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/me\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/me\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"patch":{"tags":["Account"],"summary":"Update the account profile","description":"The name is the only field that can change.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Trimmed before it is checked. It cannot contain < or > or control characters."}},"required":["name"]}}}},"responses":{"200":{"description":"The updated profile","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"tier":{"type":"string","enum":["free","pro","enterprise"]}},"required":["customer_id","email","tier"]}},"required":["success","data"]}}}},"400":{"description":"No body, no name, or an invalid name (BAD_REQUEST, VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"500":{"description":"The update failed (INTERNAL_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/me\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/me\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/me/email-preferences":{"get":{"tags":["Account"],"summary":"Get email preferences","responses":{"200":{"description":"The caller's email preferences","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"security_alerts":{"type":"boolean"},"product_updates":{"type":"boolean"}},"required":["security_alerts","product_updates"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"503":{"description":"Email preferences are not available on this deployment (UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/me/email-preferences`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me/email-preferences`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/me/email-preferences\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/me/email-preferences\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"put":{"tags":["Account"],"summary":"Update email preferences","description":"Fields left out keep their value.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"security_alerts":{"type":"boolean"},"product_updates":{"type":"boolean"}},"description":"At least one field is required."}}}},"responses":{"200":{"description":"The preferences after the update","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"security_alerts":{"type":"boolean"},"product_updates":{"type":"boolean"}},"required":["security_alerts","product_updates"]}},"required":["success","data"]}}}},"400":{"description":"No body, or no preference field (BAD_REQUEST)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"404":{"description":"The account no longer exists (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"503":{"description":"Email preferences are not available on this deployment (UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me/email-preferences`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/me/email-preferences`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/me/email-preferences\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/me/email-preferences\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/model-credentials/resolve":{"post":{"tags":["Account"],"summary":"Resolve a model credential for an agent profile","description":"Selects a model-credential seat for the profile and returns how to reach it: a CLI auth reference or an API key environment variable with its base URL. The response never holds a raw key and is never cached. Only the principal enrolled for the deployment's account-owner grant can call it; other callers, keys scoped to one sandbox and member-paid line keys get 403. A retained sandbox must be one the caller owns personally.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"selection":{"type":"object","properties":{"runId":{"type":"string","minLength":1,"maxLength":256},"nodeId":{"type":"string","minLength":1,"maxLength":256},"stageIndex":{"type":"integer","minimum":0},"segmentIndex":{"type":"integer","minimum":0},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["all-eligible"]}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["seat"]},"id":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","id"],"additionalProperties":false}]},"excludedSeatIds":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"maxItems":256},"resumeSeatId":{"type":"string","minLength":1,"maxLength":256},"minimumValidUntil":{"type":"string","format":"date-time"},"retainedEnvironmentId":{"type":"string","minLength":1,"maxLength":256},"requiresNativeTurnGrant":{"type":"boolean"}},"required":["runId","nodeId","stageIndex","segmentIndex","profile","selector","excludedSeatIds","minimumValidUntil","requiresNativeTurnGrant"],"additionalProperties":false}},"required":["selection"],"additionalProperties":false}}}},"responses":{"200":{"description":"The selected seat and how to reach it","content":{"application/json":{"schema":{"type":"object","properties":{"seatId":{"type":"string","minLength":1,"maxLength":256},"credentials":{"anyOf":[{"type":"object","properties":{"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false}},"required":["cliAuth"],"additionalProperties":false},{"type":"object","properties":{"apiKeyEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"baseUrl":{"type":"string","format":"uri"}},"required":["apiKeyEnv","baseUrl"],"additionalProperties":false}]}},"required":["seatId","credentials"],"additionalProperties":false}}}},"400":{"description":"The selection is invalid, or the seat owner refused it","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["provider_quota_exhausted","upstream_unavailable","model_credential_refused"]},"reason":{"type":"string","enum":["exhausted","unknown","held","busy","unsupported_route","forbidden","revoked"]},"resetAt":{"type":"string","format":"date-time"},"recheckAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["code","reason"]}},"required":["error"]}]}}}},"401":{"description":"Missing or invalid credential, or the seat owner refused the grant","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["provider_quota_exhausted","upstream_unavailable","model_credential_refused"]},"reason":{"type":"string","enum":["exhausted","unknown","held","busy","unsupported_route","forbidden","revoked"]},"resetAt":{"type":"string","format":"date-time"},"recheckAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["code","reason"]}},"required":["error"]},{"$ref":"#/components/schemas/CredentialRefusal"}]}}}},"403":{"description":"The caller cannot use this grant, or cannot write the retained sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["provider_quota_exhausted","upstream_unavailable","model_credential_refused"]},"reason":{"type":"string","enum":["exhausted","unknown","held","busy","unsupported_route","forbidden","revoked"]},"resetAt":{"type":"string","format":"date-time"},"recheckAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["code","reason"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy request limit","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"The seat owner rate-limited the selection","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["provider_quota_exhausted","upstream_unavailable","model_credential_refused"]},"reason":{"type":"string","enum":["exhausted","unknown","held","busy","unsupported_route","forbidden","revoked"]},"resetAt":{"type":"string","format":"date-time"},"recheckAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["code","reason"]}},"required":["error"]}}}},"503":{"description":"The seat owner is unavailable or has no seat to give","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["provider_quota_exhausted","upstream_unavailable","model_credential_refused"]},"reason":{"type":"string","enum":["exhausted","unknown","held","busy","unsupported_route","forbidden","revoked"]},"resetAt":{"type":"string","format":"date-time"},"recheckAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["code","reason"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/model-credentials/resolve`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/model-credentials/resolve`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/model-credentials/resolve\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/model-credentials/resolve\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/notifications":{"get":{"tags":["Notifications"],"summary":"List notifications","parameters":[{"schema":{"type":["boolean","null"],"default":false},"required":false,"name":"unread","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of notifications","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"read":{"type":"boolean"},"metadata":{"type":"object","additionalProperties":{}},"createdAt":{"type":"string"}},"required":["id","type","title","message","read","createdAt"]}},"unreadCount":{"type":"number"}},"required":["notifications","unreadCount"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/notifications`, baseUrl);\nconst unread = process.env.SANDBOX_UNREAD;\nif (unread !== undefined) url.searchParams.set(\"unread\", unread);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/notifications`, baseUrl);\nconst unread = process.env.SANDBOX_UNREAD;\nif (unread !== undefined) url.searchParams.set(\"unread\", unread);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/notifications\"\nquery = {}\n_unread = os.environ.get(\"SANDBOX_UNREAD\")\nif _unread is not None: query[\"unread\"] = _unread\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/notifications\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_UNREAD:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}unread=${SANDBOX_UNREAD}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/notifications/read-all":{"post":{"tags":["Notifications"],"summary":"Mark all notifications as read","responses":{"200":{"description":"All marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/notifications/read-all`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/notifications/read-all`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/notifications/read-all\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/notifications/read-all\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/notifications/{id}/read":{"post":{"tags":["Notifications"],"summary":"Mark a notification as read","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Marked as read","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/notifications/${encodeURIComponent(id)}/read`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/notifications/${encodeURIComponent(id)}/read`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/notifications/{quote(_id or '', safe='')}/read\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/notifications/${SANDBOX_ID:?Set SANDBOX_ID}/read\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/plan-followups/{planId}/admission":{"post":{"tags":["Durable plans"],"summary":"Admit a plan follow-up turn","description":"Authenticated by the x-tangle-plan-followup-credential header, not by a bearer token.","security":[],"parameters":[{"schema":{"type":"string","description":"Plan ID"},"required":true,"description":"Plan ID","name":"planId","in":"path"},{"schema":{"type":"string","description":"Follow-up credential from the plan decision's resume"},"required":true,"description":"Follow-up credential from the plan decision's resume","name":"x-tangle-plan-followup-credential","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"turnId":{"type":"string","minLength":1,"maxLength":256},"executionId":{"type":"string","minLength":1,"maxLength":256},"continuation":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"plan":{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"}},"required":["id","revision","body","submittedAt"],"additionalProperties":false},"sourceToolCallId":{"type":"string","minLength":1,"maxLength":256},"decision":{"oneOf":[{"type":"object","properties":{"outcome":{"type":"string","enum":["approved"]}},"required":["outcome"],"additionalProperties":false},{"type":"object","properties":{"outcome":{"type":"string","enum":["rejected"]},"feedback":{"type":"string","minLength":1,"maxLength":32768}},"required":["outcome","feedback"],"additionalProperties":false}]},"providerState":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["claude-code"]},"version":{"type":"number","enum":[1]},"nativeSessionId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","nativeSessionId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["codex"]},"version":{"type":"number","enum":[1]},"threadId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","threadId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["opencode"]},"version":{"type":"number","enum":[1]}},"required":["kind","version"],"additionalProperties":false}]}},"required":["version","plan","sourceToolCallId","decision","providerState"],"additionalProperties":false}},"required":["turnId","executionId","continuation"],"additionalProperties":false}}}},"responses":{"200":{"description":"Follow-up admitted","content":{"application/json":{"schema":{"type":"object","properties":{"admitted":{"type":"boolean","enum":[true]}},"required":["admitted"]}}}},"400":{"description":"Invalid admission (INVALID_REQUEST)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The credential is missing or invalid (PLAN_FOLLOWUP_CREDENTIAL_INVALID)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The follow-up cannot be admitted in its current state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-followups/${encodeURIComponent(planId)}/admission`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_followup_credential = process.env.SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL;\nif (!x_tangle_plan_followup_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL\");\nif (x_tangle_plan_followup_credential !== undefined) headers[\"x-tangle-plan-followup-credential\"] = x_tangle_plan_followup_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-followups/${encodeURIComponent(planId)}/admission`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_followup_credential = process.env.SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL;\nif (!x_tangle_plan_followup_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL\");\nif (x_tangle_plan_followup_credential !== undefined) headers[\"x-tangle-plan-followup-credential\"] = x_tangle_plan_followup_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_planId = os.environ.get(\"SANDBOX_PLAN_ID\")\nif not _planId: raise SystemExit(\"Set SANDBOX_PLAN_ID\")\nurl = f\"{base_url}/v1/plan-followups/{quote(_planId or '', safe='')}/admission\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_x_tangle_plan_followup_credential = os.environ.get(\"SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL\")\nif not _x_tangle_plan_followup_credential: raise SystemExit(\"Set SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL\")\nif _x_tangle_plan_followup_credential is not None: headers[\"x-tangle-plan-followup-credential\"] = _x_tangle_plan_followup_credential\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/plan-followups/${SANDBOX_PLAN_ID:?Set SANDBOX_PLAN_ID}/admission\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"x-tangle-plan-followup-credential: ${SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL:?Set SANDBOX_X_TANGLE_PLAN_FOLLOWUP_CREDENTIAL}\""}]}},"/v1/plan-submissions":{"post":{"tags":["Durable plans"],"summary":"Submit a plan for approval","description":"Authenticated by the x-tangle-plan-submission-credential header, not by a bearer token. A repeat with the same sourceTurnId and sourceToolCallId returns the same plan. Approvers cannot see the plan until it is confirmed. A session whose backend isolates profile secrets is refused.","security":[],"parameters":[{"schema":{"type":"string","description":"Plan submission credential issued for the agent session"},"required":true,"description":"Plan submission credential issued for the agent session","name":"x-tangle-plan-submission-credential","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"projectRef":{"type":"string","minLength":1,"maxLength":256},"agentSessionId":{"type":"string","minLength":1,"maxLength":256},"sourceTurnId":{"type":"string","minLength":1,"maxLength":256},"sourceToolCallId":{"type":"string","minLength":1,"maxLength":256},"title":{"type":"string","minLength":1,"maxLength":512},"body":{"type":"string","minLength":1,"maxLength":524288},"providerState":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["claude-code"]},"version":{"type":"number","enum":[1]},"nativeSessionId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","nativeSessionId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["codex"]},"version":{"type":"number","enum":[1]},"threadId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","threadId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["opencode"]},"version":{"type":"number","enum":[1]}},"required":["kind","version"],"additionalProperties":false}]},"backendConfig":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0},"mode":{"type":"string","enum":["api","cli"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"authMode":{"type":"string","enum":["api-key","oauth"]},"apiKeyEnv":{"type":"string","maxLength":128,"pattern":"^[A-Z_][A-Z0-9_]*$"}},"additionalProperties":false},"server":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"hostname":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}}},"required":["mcp"],"additionalProperties":false},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","properties":{"containerType":{"type":"string","minLength":1,"maxLength":256},"profileSecretIsolation":{"type":"boolean","const":true},"isolationPrincipal":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"traceAttributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"additionalProperties":{"type":"string","maxLength":1024}},"telemetry":{"not":{}}},"additionalProperties":false}},"additionalProperties":false}},"required":["projectRef","agentSessionId","sourceTurnId","sourceToolCallId","body","providerState"],"additionalProperties":false}}}},"responses":{"201":{"description":"Plan submitted","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"}},"required":["id","revision","body","submittedAt"],"additionalProperties":false}},"required":["plan"]}}}},"400":{"description":"Invalid submission (INVALID_REQUEST, PLAN_BODY_REQUIRED, PLAN_SOURCE_ID_REQUIRED, PLAN_PROVIDER_STATE_INVALID)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The credential is missing, invalid or expired (PLAN_SUBMISSION_CREDENTIAL_INVALID, PLAN_SUBMISSION_CREDENTIAL_EXPIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The backend isolates profile secrets (PROFILE_SECRET_ISOLATION_UNAVAILABLE)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"reason":{"type":"string"}},"required":["reason"]}]}}}},"409":{"description":"The plan cannot take this change in its current state (PLAN_SUBMISSION_CONFLICT, PLAN_NOT_READY, PLAN_SUPERSEDED, PLAN_WITHDRAWN, PLAN_ALREADY_DECIDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/plan-submissions`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/plan-submissions`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/plan-submissions\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_x_tangle_plan_submission_credential = os.environ.get(\"SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif not _x_tangle_plan_submission_credential: raise SystemExit(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif _x_tangle_plan_submission_credential is not None: headers[\"x-tangle-plan-submission-credential\"] = _x_tangle_plan_submission_credential\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/plan-submissions\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"x-tangle-plan-submission-credential: ${SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL:?Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL}\""}]}},"/v1/plan-submissions/{planId}/confirmation":{"post":{"tags":["Durable plans"],"summary":"Confirm a submitted plan","description":"Authenticated by the x-tangle-plan-submission-credential header, not by a bearer token. Makes the plan visible to approvers and supersedes the session's earlier plans.","security":[],"parameters":[{"schema":{"type":"string","description":"Plan ID"},"required":true,"description":"Plan ID","name":"planId","in":"path"},{"schema":{"type":"string","description":"Plan submission credential issued for the agent session"},"required":true,"description":"Plan submission credential issued for the agent session","name":"x-tangle-plan-submission-credential","in":"header"}],"responses":{"200":{"description":"Plan confirmed","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"}},"required":["id","revision","body","submittedAt"],"additionalProperties":false}},"required":["plan"]}}}},"401":{"description":"The credential is missing, invalid or expired (PLAN_SUBMISSION_CREDENTIAL_INVALID, PLAN_SUBMISSION_CREDENTIAL_EXPIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The plan cannot take this change in its current state (PLAN_SUBMISSION_CONFLICT, PLAN_NOT_READY, PLAN_SUPERSEDED, PLAN_WITHDRAWN, PLAN_ALREADY_DECIDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-submissions/${encodeURIComponent(planId)}/confirmation`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-submissions/${encodeURIComponent(planId)}/confirmation`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_planId = os.environ.get(\"SANDBOX_PLAN_ID\")\nif not _planId: raise SystemExit(\"Set SANDBOX_PLAN_ID\")\nurl = f\"{base_url}/v1/plan-submissions/{quote(_planId or '', safe='')}/confirmation\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_x_tangle_plan_submission_credential = os.environ.get(\"SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif not _x_tangle_plan_submission_credential: raise SystemExit(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif _x_tangle_plan_submission_credential is not None: headers[\"x-tangle-plan-submission-credential\"] = _x_tangle_plan_submission_credential\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/plan-submissions/${SANDBOX_PLAN_ID:?Set SANDBOX_PLAN_ID}/confirmation\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"x-tangle-plan-submission-credential: ${SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL:?Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL}\""}]}},"/v1/plan-submissions/{planId}/withdrawal":{"post":{"tags":["Durable plans"],"summary":"Withdraw a submitted plan","description":"Authenticated by the x-tangle-plan-submission-credential header, not by a bearer token.","security":[],"parameters":[{"schema":{"type":"string","description":"Plan ID"},"required":true,"description":"Plan ID","name":"planId","in":"path"},{"schema":{"type":"string","description":"Plan submission credential issued for the agent session"},"required":true,"description":"Plan submission credential issued for the agent session","name":"x-tangle-plan-submission-credential","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":2000}},"required":["reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Plan withdrawn","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/DurablePlan"}},"required":["plan"]}}}},"400":{"description":"Invalid request (INVALID_REQUEST, PLAN_WITHDRAWAL_REASON_REQUIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"The credential is missing, invalid or expired (PLAN_SUBMISSION_CREDENTIAL_INVALID, PLAN_SUBMISSION_CREDENTIAL_EXPIRED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The plan cannot take this change in its current state (PLAN_SUBMISSION_CONFLICT, PLAN_NOT_READY, PLAN_SUPERSEDED, PLAN_WITHDRAWN, PLAN_ALREADY_DECIDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-submissions/${encodeURIComponent(planId)}/withdrawal`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/plan-submissions/${encodeURIComponent(planId)}/withdrawal`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst x_tangle_plan_submission_credential = process.env.SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL;\nif (!x_tangle_plan_submission_credential) throw new Error(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\");\nif (x_tangle_plan_submission_credential !== undefined) headers[\"x-tangle-plan-submission-credential\"] = x_tangle_plan_submission_credential;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_planId = os.environ.get(\"SANDBOX_PLAN_ID\")\nif not _planId: raise SystemExit(\"Set SANDBOX_PLAN_ID\")\nurl = f\"{base_url}/v1/plan-submissions/{quote(_planId or '', safe='')}/withdrawal\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_x_tangle_plan_submission_credential = os.environ.get(\"SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif not _x_tangle_plan_submission_credential: raise SystemExit(\"Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL\")\nif _x_tangle_plan_submission_credential is not None: headers[\"x-tangle-plan-submission-credential\"] = _x_tangle_plan_submission_credential\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/plan-submissions/${SANDBOX_PLAN_ID:?Set SANDBOX_PLAN_ID}/withdrawal\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"x-tangle-plan-submission-credential: ${SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL:?Set SANDBOX_X_TANGLE_PLAN_SUBMISSION_CREDENTIAL}\""}]}},"/v1/pricing/egress":{"get":{"tags":["Pricing"],"summary":"Get egress pricing","description":"Each plan's monthly egress allowance and the price beyond it. Public: no credential is required. Outbound bytes from each sandbox are counted per UTC calendar month; inbound traffic is free. Every plan's allowance is per account (`scope: account`), shared by its sandboxes: `overage: billed` plans pay `usdPerGb` per decimal GB (10^9 bytes) beyond `includedBytes`; `overage: refused` plans are refused new sandboxes and prompts at it and never charged. Computed from the allowances quota admission enforces and the rates this deployment bills.","responses":{"200":{"description":"Egress terms retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxEgressTermsResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/pricing/egress`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/pricing/egress`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/pricing/egress\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/pricing/egress\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/pricing/sandboxes":{"get":{"tags":["Pricing"],"summary":"Get the sandbox rate card","description":"USD per vCPU-hour, per GB-hour of memory, per GB-hour of disk, and the hourly minimum, for the pool this deployment bills sandboxes in. A sandbox is billed vcpu*cpuCoreHourUsd + memoryGB*memoryGBHourUsd + diskGB*diskGBHourUsd per hour, floored at minChargeHourUsd. `source` is `pool-derived` when the numbers come from the configured pool's own host cost and sellable capacity, and `reference-constants` when no pool cost is configured and the deployment prices on the pricing engine's reference server instead. Public: no credential is required.","responses":{"200":{"description":"Rate card retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxPricingResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/pricing/sandboxes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/pricing/sandboxes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/pricing/sandboxes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/pricing/sandboxes\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates":{"post":{"tags":["Public templates"],"summary":"Publish a snapshot as a public template","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"slug":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":280,"default":""},"websiteUrl":{"type":"string","maxLength":2048,"format":"uri"},"snapshotId":{"type":"string","minLength":1},"sourceSandboxId":{"type":"string","minLength":1},"teamId":{"type":"string"},"readmeMarkdown":{"type":"string","maxLength":200000,"default":""},"tags":{"type":"array","items":{"type":"string"},"default":[]},"releaseNotes":{"type":"string","default":""},"forkedFromTemplateId":{"type":"string"}},"required":["name","snapshotId","sourceSandboxId"]}}}},"responses":{"202":{"description":"Publish queued","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string"},"artifactId":{"type":"string"},"templateId":{"type":["string","null"]},"versionId":{"type":["string","null"]},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"createdByCustomerId":{"type":"string"},"completedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","artifactId","templateId","versionId","status","createdByCustomerId","completedAt","createdAt","updatedAt"]},"artifact":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"sourceSnapshotId":{"type":"string"},"sourceSandboxId":{"type":"string"},"derivedSnapshotId":{"type":["string","null"]},"scannerName":{"type":["string","null"]},"scannerVersion":{"type":["string","null"]},"scannerCompletedAt":{"type":["string","null"]},"findingsSummary":{"type":["object","null"],"properties":{"totalFindings":{"type":"number"},"byRule":{"type":"object","additionalProperties":{"type":"number"}},"scrubbedPaths":{"type":"array","items":{"type":"string"}}},"required":["totalFindings","byRule","scrubbedPaths"]},"errorMessage":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","sourceSnapshotId","sourceSandboxId","derivedSnapshotId","scannerName","scannerVersion","scannerCompletedAt","findingsSummary","errorMessage","createdAt","updatedAt"]}},"required":["job","artifact"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Snapshot not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/public-templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/public-templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/public-templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"get":{"tags":["Public templates"],"summary":"Search public templates","parameters":[{"schema":{"type":"string"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string"},"required":false,"name":"tag","in":"query"},{"schema":{"type":["boolean","null"]},"required":false,"name":"featured","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"websiteUrl":{"type":["string","null"]},"ownerTeamId":{"type":["string","null"]},"forkedFromTemplateId":{"type":["string","null"]},"latestVersionId":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"featuredRank":{"type":["number","null"]},"forkCount":{"type":"number"},"sandboxCount":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"publishedAt":{"type":"string"},"latestVersion":{"type":["object","null"],"properties":{"id":{"type":"string"},"templateId":{"type":"string"},"versionNumber":{"type":"number"},"snapshotId":{"type":"string"},"publicArtifactId":{"type":["string","null"]},"sourceSnapshotId":{"type":"string"},"cellId":{"type":"string"},"readmeMarkdown":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"releaseNotes":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","templateId","versionNumber","snapshotId","publicArtifactId","sourceSnapshotId","cellId","readmeMarkdown","tags","releaseNotes","createdAt"]}},"required":["id","slug","name","description","websiteUrl","ownerTeamId","forkedFromTemplateId","latestVersionId","isFeatured","featuredRank","forkCount","sandboxCount","createdAt","updatedAt","publishedAt","latestVersion"]}}},"required":["templates"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/public-templates`, baseUrl);\nconst q = process.env.SANDBOX_Q;\nif (q !== undefined) url.searchParams.set(\"q\", q);\nconst tag = process.env.SANDBOX_TAG;\nif (tag !== undefined) url.searchParams.set(\"tag\", tag);\nconst featured = process.env.SANDBOX_FEATURED;\nif (featured !== undefined) url.searchParams.set(\"featured\", featured);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/public-templates`, baseUrl);\nconst q = process.env.SANDBOX_Q;\nif (q !== undefined) url.searchParams.set(\"q\", q);\nconst tag = process.env.SANDBOX_TAG;\nif (tag !== undefined) url.searchParams.set(\"tag\", tag);\nconst featured = process.env.SANDBOX_FEATURED;\nif (featured !== undefined) url.searchParams.set(\"featured\", featured);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/public-templates\"\nquery = {}\n_q = os.environ.get(\"SANDBOX_Q\")\nif _q is not None: query[\"q\"] = _q\n_tag = os.environ.get(\"SANDBOX_TAG\")\nif _tag is not None: query[\"tag\"] = _tag\n_featured = os.environ.get(\"SANDBOX_FEATURED\")\nif _featured is not None: query[\"featured\"] = _featured\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_offset = os.environ.get(\"SANDBOX_OFFSET\")\nif _offset is not None: query[\"offset\"] = _offset\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_Q:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}q=${SANDBOX_Q}\"\nfi\nif [ -n \"${SANDBOX_TAG:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}tag=${SANDBOX_TAG}\"\nfi\nif [ -n \"${SANDBOX_FEATURED:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}featured=${SANDBOX_FEATURED}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_OFFSET:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}offset=${SANDBOX_OFFSET}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates/artifacts/{artifactId}":{"get":{"tags":["Public templates"],"summary":"Get public template publish artifact status","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"artifactId","in":"path"}],"responses":{"200":{"description":"Artifact","content":{"application/json":{"schema":{"type":"object","properties":{"artifact":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"sourceSnapshotId":{"type":"string"},"sourceSandboxId":{"type":"string"},"derivedSnapshotId":{"type":["string","null"]},"scannerName":{"type":["string","null"]},"scannerVersion":{"type":["string","null"]},"scannerCompletedAt":{"type":["string","null"]},"findingsSummary":{"type":["object","null"],"properties":{"totalFindings":{"type":"number"},"byRule":{"type":"object","additionalProperties":{"type":"number"}},"scrubbedPaths":{"type":"array","items":{"type":"string"}}},"required":["totalFindings","byRule","scrubbedPaths"]},"errorMessage":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","sourceSnapshotId","sourceSandboxId","derivedSnapshotId","scannerName","scannerVersion","scannerCompletedAt","findingsSummary","errorMessage","createdAt","updatedAt"]}},"required":["artifact"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst artifactId = process.env.SANDBOX_ARTIFACT_ID;\nif (!artifactId) throw new Error(\"Set SANDBOX_ARTIFACT_ID\");\nconst url = new URL(`/v1/public-templates/artifacts/${encodeURIComponent(artifactId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst artifactId = process.env.SANDBOX_ARTIFACT_ID;\nif (!artifactId) throw new Error(\"Set SANDBOX_ARTIFACT_ID\");\nconst url = new URL(`/v1/public-templates/artifacts/${encodeURIComponent(artifactId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_artifactId = os.environ.get(\"SANDBOX_ARTIFACT_ID\")\nif not _artifactId: raise SystemExit(\"Set SANDBOX_ARTIFACT_ID\")\nurl = f\"{base_url}/v1/public-templates/artifacts/{quote(_artifactId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/artifacts/${SANDBOX_ARTIFACT_ID:?Set SANDBOX_ARTIFACT_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates/featured":{"get":{"tags":["Public templates"],"summary":"List featured public templates","responses":{"200":{"description":"Templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"websiteUrl":{"type":["string","null"]},"ownerTeamId":{"type":["string","null"]},"forkedFromTemplateId":{"type":["string","null"]},"latestVersionId":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"featuredRank":{"type":["number","null"]},"forkCount":{"type":"number"},"sandboxCount":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"publishedAt":{"type":"string"},"latestVersion":{"type":["object","null"],"properties":{"id":{"type":"string"},"templateId":{"type":"string"},"versionNumber":{"type":"number"},"snapshotId":{"type":"string"},"publicArtifactId":{"type":["string","null"]},"sourceSnapshotId":{"type":"string"},"cellId":{"type":"string"},"readmeMarkdown":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"releaseNotes":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","templateId","versionNumber","snapshotId","publicArtifactId","sourceSnapshotId","cellId","readmeMarkdown","tags","releaseNotes","createdAt"]}},"required":["id","slug","name","description","websiteUrl","ownerTeamId","forkedFromTemplateId","latestVersionId","isFeatured","featuredRank","forkCount","sandboxCount","createdAt","updatedAt","publishedAt","latestVersion"]}}},"required":["templates"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/public-templates/featured`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/public-templates/featured`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/public-templates/featured\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/featured\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates/publish-jobs/{jobId}":{"get":{"tags":["Public templates"],"summary":"Get public template publish job status","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"jobId","in":"path"}],"responses":{"200":{"description":"Publish job","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string"},"artifactId":{"type":"string"},"templateId":{"type":["string","null"]},"versionId":{"type":["string","null"]},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"createdByCustomerId":{"type":"string"},"completedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","artifactId","templateId","versionId","status","createdByCustomerId","completedAt","createdAt","updatedAt"]}},"required":["job"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/public-templates/publish-jobs/${encodeURIComponent(jobId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/public-templates/publish-jobs/${encodeURIComponent(jobId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_jobId = os.environ.get(\"SANDBOX_JOB_ID\")\nif not _jobId: raise SystemExit(\"Set SANDBOX_JOB_ID\")\nurl = f\"{base_url}/v1/public-templates/publish-jobs/{quote(_jobId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/publish-jobs/${SANDBOX_JOB_ID:?Set SANDBOX_JOB_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates/{id}":{"get":{"tags":["Public templates"],"summary":"Get a public template","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"websiteUrl":{"type":["string","null"]},"ownerTeamId":{"type":["string","null"]},"forkedFromTemplateId":{"type":["string","null"]},"latestVersionId":{"type":["string","null"]},"isFeatured":{"type":"boolean"},"featuredRank":{"type":["number","null"]},"forkCount":{"type":"number"},"sandboxCount":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"publishedAt":{"type":"string"},"latestVersion":{"type":["object","null"],"properties":{"id":{"type":"string"},"templateId":{"type":"string"},"versionNumber":{"type":"number"},"snapshotId":{"type":"string"},"publicArtifactId":{"type":["string","null"]},"sourceSnapshotId":{"type":"string"},"cellId":{"type":"string"},"readmeMarkdown":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"releaseNotes":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","templateId","versionNumber","snapshotId","publicArtifactId","sourceSnapshotId","cellId","readmeMarkdown","tags","releaseNotes","createdAt"]}},"required":["id","slug","name","description","websiteUrl","ownerTeamId","forkedFromTemplateId","latestVersionId","isFeatured","featuredRank","forkCount","sandboxCount","createdAt","updatedAt","publishedAt","latestVersion"]}},"required":["template"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/public-templates/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/public-templates/{id}/versions":{"get":{"tags":["Public templates"],"summary":"List versions for a public template","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Versions","content":{"application/json":{"schema":{"type":"object","properties":{"versions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"templateId":{"type":"string"},"versionNumber":{"type":"number"},"snapshotId":{"type":"string"},"publicArtifactId":{"type":["string","null"]},"sourceSnapshotId":{"type":"string"},"cellId":{"type":"string"},"readmeMarkdown":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"releaseNotes":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","templateId","versionNumber","snapshotId","publicArtifactId","sourceSnapshotId","cellId","readmeMarkdown","tags","releaseNotes","createdAt"]}}},"required":["versions"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}/versions`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}/versions`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/public-templates/{quote(_id or '', safe='')}/versions\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/${SANDBOX_ID:?Set SANDBOX_ID}/versions\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Public templates"],"summary":"Publish a new version for a public template","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"snapshotId":{"type":"string","minLength":1},"sourceSandboxId":{"type":"string","minLength":1},"readmeMarkdown":{"type":"string","maxLength":200000,"default":""},"tags":{"type":"array","items":{"type":"string"},"default":[]},"releaseNotes":{"type":"string","default":""}},"required":["snapshotId","sourceSandboxId"]}}}},"responses":{"202":{"description":"Version publish queued","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string"},"artifactId":{"type":"string"},"templateId":{"type":["string","null"]},"versionId":{"type":["string","null"]},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"createdByCustomerId":{"type":"string"},"completedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","artifactId","templateId","versionId","status","createdByCustomerId","completedAt","createdAt","updatedAt"]},"artifact":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","scanning","blocked","ready","failed"]},"sourceSnapshotId":{"type":"string"},"sourceSandboxId":{"type":"string"},"derivedSnapshotId":{"type":["string","null"]},"scannerName":{"type":["string","null"]},"scannerVersion":{"type":["string","null"]},"scannerCompletedAt":{"type":["string","null"]},"findingsSummary":{"type":["object","null"],"properties":{"totalFindings":{"type":"number"},"byRule":{"type":"object","additionalProperties":{"type":"number"}},"scrubbedPaths":{"type":"array","items":{"type":"string"}}},"required":["totalFindings","byRule","scrubbedPaths"]},"errorMessage":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","status","sourceSnapshotId","sourceSandboxId","derivedSnapshotId","scannerName","scannerVersion","scannerCompletedAt","findingsSummary","errorMessage","createdAt","updatedAt"]}},"required":["job","artifact"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}/versions`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/public-templates/${encodeURIComponent(id)}/versions`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/public-templates/{quote(_id or '', safe='')}/versions\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/public-templates/${SANDBOX_ID:?Set SANDBOX_ID}/versions\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/router-models":{"get":{"tags":["Backends"],"summary":"List the Router model catalog","description":"The Tangle Router's `GET /v1/models` rows, read with the caller's managed Router key, so the list is the models this caller can run.","responses":{"200":{"description":"Router model catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouterModelsResponse"}}}},"502":{"description":"The Router refused or failed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["router_catalog_unavailable"]}},"required":["error","code"]}}}},"503":{"description":"No Router key can be minted","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["router_catalog_unavailable"]}},"required":["error","code"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/router-models`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/router-models`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/router-models\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/router-models\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes":{"post":{"summary":"Create a new sandbox","tags":["Sandboxes"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":256},"ownerContext":{"type":"string","enum":["inherit","isolated"]},"funding":{"type":"string","enum":["wallet","referral_credit"]},"driver":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"tangle"},"enableCriu":{"type":"boolean"},"preferredRegion":{"type":"string","minLength":1,"maxLength":128}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["docker","host-agent"]},"runtimeBackend":{"type":"string","enum":["docker","firecracker"]},"enableCriu":{"type":"boolean"},"preferredRegion":{"type":"string","minLength":1,"maxLength":128}},"required":["type"],"additionalProperties":false}]},"runtime":{"type":"string","enum":["docker","firecracker"]},"environment":{"type":"string"},"git":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"ref":{"type":"string"},"depth":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sparse":{"type":"array","items":{"type":"string"}},"auth":{"type":"object","properties":{"token":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema0"},"grant":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema1"}}}},"required":["url"]},"cwd":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema2"},"bare":{"type":"boolean"},"agent":{"type":"boolean"},"requireNativeSessionCapture":{"type":"boolean"},"sessionCapture":{"type":"string","const":"discard"},"skipWarmPool":{"type":"boolean"},"capacityClass":{"type":"string","const":"spare_only"},"confidential":{"type":"object","properties":{"tee":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"sealed":{"type":"boolean"},"attestationNonce":{"type":"string","pattern":"^(?:0x)?[0-9a-fA-F]{64,128}$"},"attestationRefresh":{"type":"boolean"}}},"env":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema3"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema4"}},"envRecovery":{"type":"object","properties":{"callerResupplies":{"maxItems":128,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema5"}}},"required":["callerResupplies"],"additionalProperties":false},"resources":{"type":"object","properties":{"cpuCores":{"type":"number","exclusiveMinimum":0},"memoryMB":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema6"},"diskGB":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema6"},"accelerator":{"type":"object","properties":{"kind":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"count":{"default":1,"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"vramGb":{"type":"number","exclusiveMinimum":0}},"required":["kind"],"additionalProperties":false},"advisory":{"type":"boolean"},"advisoryFields":{"type":"array","items":{"type":"string","enum":["cpuCores","memoryMB","diskGB"]}}},"additionalProperties":false},"gpuLease":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z][a-z0-9-]*$"},"image":{"type":"string","minLength":1,"maxLength":512},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"type":"string"}},"diskGb":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"volumeGb":{"type":"integer","minimum":0,"maximum":9007199254740991},"volumeMountPath":{"type":"string","maxLength":2048,"format":"starts_with","pattern":"^\\/.*"},"region":{"type":"string","minLength":1,"maxLength":128},"manifest":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"command":{"minItems":1,"maxItems":256,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema7"}},"ports":{"maxItems":32,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema8"}},"endpoint":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"healthPath":{"type":"string","maxLength":2048,"pattern":"^\\/(?![/\\\\])"}},"required":["port"],"additionalProperties":false},"allowSpot":{"type":"boolean"},"maxHourlyUsd":{"type":"number","exclusiveMinimum":0},"weights":{"type":"object","properties":{"manifestSha":{"type":"string","pattern":"^[a-f0-9]{64}$"},"sizeBytes":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["manifestSha","sizeBytes"],"additionalProperties":false},"arms":{"minItems":1,"maxItems":256,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema9"}},"maxSpendUsd":{"type":"number","exclusiveMinimum":0,"maximum":10000},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":86400},"idleTimeoutSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["maxSpendUsd","maxLifetimeSeconds"],"additionalProperties":false},"backend":{"type":"object","properties":{"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0,"maximum":9007199254740991},"mode":{"type":"string","enum":["api","cli"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"authMode":{"type":"string","enum":["api-key","oauth"]},"authFiles":{"maxItems":128,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema10"}},"apiKeyEnv":{"type":"string","maxLength":128,"pattern":"^[A-Z_][A-Z0-9_]*$"},"webSearch":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{"provider":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema11"},"engine":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema12"},"maxResults":{"type":"integer","minimum":1,"maximum":25},"maxTotalResults":{"type":"integer","minimum":1,"maximum":100},"searchContextSize":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema13"},"searchPrompt":{"type":"string","maxLength":2000},"includeDomains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema14"},"excludeDomains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema14"},"searchRecency":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema16"}},"additionalProperties":false}]},"plugins":{"maxItems":1,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema17"}},"routing":{"type":"object","properties":{"only":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema18"},"allowFallbacks":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema20"},"requireModel":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema21"}},"additionalProperties":false}},"additionalProperties":false},"server":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"hostname":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false},"profile":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/AgentProfile"},"runtimeAttachments":{"type":"object","properties":{"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}}},"required":["mcp"],"additionalProperties":false,"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema111"},"interactions":{"type":"object","properties":{"permission":{"type":"boolean"},"question":{"type":"boolean"},"plan":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","properties":{"containerType":{"type":"string","minLength":1,"maxLength":256},"profileSecretIsolation":{"type":"boolean","const":true},"isolationPrincipal":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"traceAttributes":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"additionalProperties":{"type":"string","maxLength":1024}},"telemetry":{"not":{}}},"additionalProperties":false}},"additionalProperties":false},"sshEnabled":{"type":"boolean"},"sshPublicKeys":{"maxItems":20,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema119"}},"sshKeyIds":{"maxItems":20,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema120"}},"webTerminalEnabled":{"type":"boolean"},"proxySecrets":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema52"},"secrets":{"anyOf":[{"anyOf":[{"type":"string","const":"all"},{"maxItems":100,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema121"}}]},{"type":"null"}]},"maxLifetimeSeconds":{"type":"number"},"idleTimeoutSeconds":{"type":"number"},"coldTimeoutSeconds":{"type":"integer","minimum":1,"maximum":9007199254740},"deleteAfterStoppedSeconds":{"type":"integer","minimum":300,"maximum":9007199254740},"retain":{"type":"object","properties":{"paths":{"maxItems":64,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema122"}}},"required":["paths"],"additionalProperties":false},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"storage":{"type":"object","properties":{"type":{"type":"string","enum":["s3","gcs","r2"]},"bucket":{"type":"string"},"endpoint":{"type":"string"},"region":{"type":"string"},"credentials":{"type":"object","properties":{"accessKeyId":{"type":"string"},"secretAccessKey":{"type":"string"}},"required":["accessKeyId","secretAccessKey"]},"prefix":{"type":"string"}},"required":["type","bucket","credentials"]},"ephemeral":{"type":"boolean"},"fromSnapshot":{"type":"string"},"fromSandboxId":{"type":"string"},"startupScriptIds":{"maxItems":50,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema123"}},"teamId":{"type":"string","pattern":"^team_[a-f0-9]{32}$"},"capabilities":{"maxItems":8,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema124"}},"privacy":{"type":"object","properties":{"egress":{"type":"string","enum":["redact","block","off"]},"logs":{"type":"string","enum":["on","off"]}}},"compliance":{"anyOf":[{"type":"object","properties":{"hipaa":{"type":"boolean","const":true}},"required":["hipaa"],"additionalProperties":false},{"type":"array","prefixItems":[{"type":"string","const":"hipaa"}],"items":false,"minItems":1,"maxItems":1}]},"egressPolicy":{"type":"object","properties":{"mode":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema125"},"allowDomains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema126"},"includeImplicitDomains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema129"}},"required":["mode"],"additionalProperties":false},"templateId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"publicTemplateId":{"type":"string","minLength":1},"publicTemplateVersionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255},"billingOwnerId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}},"additionalProperties":false,"$defs":{"__schema0":{"type":"string"},"__schema1":{"type":"string","const":"read"},"__schema2":{"type":"string","minLength":1,"maxLength":4096},"__schema3":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema4":{"type":"string"},"__schema5":{"type":"string","maxLength":256},"__schema6":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"__schema7":{"type":"string","maxLength":8192},"__schema8":{"type":"integer","minimum":1,"maximum":65535},"__schema9":{},"__schema10":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":4096},"content":{"type":"string","maxLength":1048576},"mode":{"type":"integer","minimum":0,"maximum":511}},"required":["path","content"],"additionalProperties":false},"__schema11":{"type":"string","enum":["perplexity","exa","you","parallel","tavily","brave","serpapi","serper","google_cse","searxng"]},"__schema12":{"type":"string","enum":["auto","native","exa","parallel","firecrawl"]},"__schema13":{"type":"string","enum":["low","medium","high"]},"__schema14":{"maxItems":100,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema15"}},"__schema15":{"type":"string","minLength":1,"maxLength":256},"__schema16":{"type":"string","enum":["day","week","month","year"]},"__schema17":{"type":"object","properties":{"id":{"type":"string","const":"web"},"provider":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema11"},"engine":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema12"},"max_results":{"type":"integer","minimum":1,"maximum":25},"max_total_results":{"type":"integer","minimum":1,"maximum":100},"search_context_size":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema13"},"search_prompt":{"type":"string","maxLength":2000},"include_domains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema14"},"exclude_domains":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema14"},"search_recency":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema16"}},"required":["id"],"additionalProperties":false},"__schema18":{"minItems":1,"maxItems":20,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema19"}},"__schema19":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,63}$"},"__schema20":{"type":"boolean"},"__schema21":{"type":"boolean"},"__schema22":{"type":"string"},"__schema23":{"type":"string"},"__schema24":{"type":"string"},"__schema25":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema26"}},"__schema26":{"type":"string"},"__schema27":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema28"}}},"additionalProperties":false},"__schema28":{"type":"string"},"__schema29":{"type":"object","properties":{"default":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema30"},"small":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema31"},"provider":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema32"},"reasoningEffort":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema33"},"maxVisibleOutputTokens":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema35"},"maxReasoningTokens":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema36"},"maxTotalOutputTokens":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema37"},"metadata":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema38"}},"additionalProperties":false},"__schema30":{"type":"string"},"__schema31":{"type":"string"},"__schema32":{"type":"string"},"__schema33":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema34"},"__schema34":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"__schema35":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"__schema36":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"__schema37":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"__schema38":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema39"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}},"__schema39":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema40":{},"__schema41":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema42"},"__schema42":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"__schema43":{"minItems":1,"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema44"}},"__schema44":{"type":"object","properties":{"harness":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema42"},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema34"},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema45"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema46"}},"permissions":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema47"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema48"}}},"required":["harness","provider","model","selector"],"additionalProperties":false},"__schema45":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema46":{"type":"boolean"},"__schema47":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema48":{"anyOf":[{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema49"},{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema50"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema49"}}]},"__schema49":{"type":"string","enum":["allow","deny","ask"]},"__schema50":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema51":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema52"},"__schema52":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema53"}},"__schema53":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema54"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema55"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema56"}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema57"},"key":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema58"}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"__schema54":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"__schema55":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"__schema56":{"type":"string","maxLength":2048},"__schema57":{"type":"string","const":"secret-ref"},"__schema58":{"type":"string","minLength":1,"maxLength":500},"__schema59":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema60"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema48"}},"__schema60":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema61":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema62"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema63"}},"__schema62":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema63":{"type":"boolean"},"__schema64":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema65"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema66"}},"__schema65":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema66":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema67"},"args":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema68"}},"env":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema69"},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema72"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string","$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema67"},"headers":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema73"},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema75"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema76"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}}},"required":["enabled"],"additionalProperties":false}]},"__schema67":{"type":"string"},"__schema68":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema57"},"key":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema58"},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]},"__schema69":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema70"},"__schema70":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema71"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema68"}},"__schema71":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema72":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema73":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema74"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema68"}},"__schema74":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema75":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema76":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema77":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema78"}},"__schema78":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema79"}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false},"__schema79":{"type":"string","minLength":1},"__schema80":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema81"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema82"}},"__schema81":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema82":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema83"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema84"}},"permissions":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema85"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema48"}},"maxSteps":{"type":"number"},"metadata":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema86"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}}},"additionalProperties":false},"__schema83":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema84":{"type":"boolean"},"__schema85":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema86":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema87":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema88"}},"files":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema89"}},"tools":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"}},"skills":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"}},"agents":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"}},"commands":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"}},"instructions":{"anyOf":[{"type":"string"},{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"__schema88":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false},"__schema89":{"type":"object","properties":{"path":{"type":"string"},"resource":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema90"},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false},"__schema90":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"__schema91":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema92"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema93"}},"__schema92":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema93":{"maxItems":1024,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema94"}},"__schema94":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema69"}},"required":["command"],"additionalProperties":false},"__schema95":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema96"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema97"}},"__schema96":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema97":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema98"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema99"}},"permissions":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema100"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema48"}},"metadata":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema101"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}}},"additionalProperties":false},"__schema98":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema99":{"type":"boolean"},"__schema100":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema101":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema102":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"__schema103":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema104"},"__schema104":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema105"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema106"}},"__schema105":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema106":{},"__schema107":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema108"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema109"}},"__schema108":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"__schema109":{"anyOf":[{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema110"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema40"}},{}]},"__schema110":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"AgentProfile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","additionalProperties":{}},"extensions":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","additionalProperties":{}},{}]}}},"additionalProperties":false},"__schema111":{},"__schema112":{"type":"string","minLength":1,"maxLength":256},"__schema113":{"type":"boolean","const":true},"__schema114":{"type":"string","pattern":"^[A-Za-z0-9_.:@-]{1,256}$"},"__schema115":{"type":"object","propertyNames":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema116"},"additionalProperties":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema117"}},"__schema116":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"},"__schema117":{"type":"string","maxLength":1024},"__schema118":{"not":{}},"__schema119":{"type":"string","minLength":1,"maxLength":4096},"__schema120":{"type":"string","minLength":1,"maxLength":200},"__schema121":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"__schema122":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema2"},"__schema123":{"type":"string"},"__schema124":{"type":"string","enum":["computer_use"]},"__schema125":{"type":"string","enum":["open","strict","blocked"]},"__schema126":{"maxItems":100,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema127"}},"__schema127":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"minItems":1,"maxItems":100,"type":"array","items":{"$ref":"#/paths/~1v1~1sandboxes/post/requestBody/content/application~1json/schema/$defs/__schema128"}}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"__schema128":{"anyOf":[{"type":"string","format":"cidrv4","pattern":"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/([0-9]|[1-2][0-9]|3[0-2])$"},{"type":"string","format":"cidrv6","pattern":"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$"}]},"__schema129":{"type":"boolean"}},"title":"CreateSandboxRequest","description":"Canonical create request. Additional API validation: isolated owner context requires a fresh personal workspace without teams, templates, snapshots, stored scripts, enabled SSH, SSH keys, stored secrets, git read grants or profile owner connections. Native auth secret references cannot be overridden by inline env and must be supported by the selected backend. Native auth and API key environment references must be selected in secrets (or secrets=all). fromSnapshot requires fromSandboxId. agent=false forbids backend, bare=true and stored scripts, and requires Docker. Egress domains must use well-formed domain patterns, and strict policies must satisfy the supported implicit-host restrictions. Bounded JSON predicates and AgentProfile refinements remain enforced.","x-tangle-runtime-validation":["Custom bounded JSON predicates and AgentProfile refinements remain enforced by the create request validator."]}}}},"responses":{"201":{"description":"Sandbox created","headers":{"Deprecation":{"schema":{"type":"string","description":"Sent when the body used the legacy driver form (`driver.type` docker or host-agent, `driver.runtimeBackend`). Deprecated on 2026-11-05 and removed from current SDK releases: send `runtime` and omit `driver`."},"required":false,"description":"Sent when the body used the legacy driver form (`driver.type` docker or host-agent, `driver.runtimeBackend`). Deprecated on 2026-11-05 and removed from current SDK releases: send `runtime` and omit `driver`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxCreateResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Platform refused the caller's credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Subscription inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed, platform refused authorization, PROFILE_SECRET_ISOLATION_UNAVAILABLE: a new sandbox cannot declare backend.metadata.profileSecretIsolation; declare it on a session of a running sandbox, or COMPLIANCE_NOT_ENTITLED: the body asks for `compliance.hipaa` but the caller's credential carries no HIPAA entitlement","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Template not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Template artifact not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Request payload too large or restore snapshot exceeds its quota","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string","enum":["PAYLOAD_TOO_LARGE"]},"message":{"type":"string"},"surface":{"type":"string"},"limitBytes":{"type":"integer","exclusiveMinimum":0},"actualBytes":{"type":"integer","exclusiveMinimum":0}},"required":["code","message","surface","limitBytes"]}},"required":["success","error"]},{"$ref":"#/components/schemas/RestoreQuotaErrorResponse"}]}}}},"422":{"description":"GIT_CLONE_REFUSED: the repository refused the clone's credentials. Terminal: a retry with the same Idempotency-Key answers the same refusal. PROFILE_FILE_MOUNT_OUTSIDE_ROOTS: a backend.profile.resources.files path is outside the sandbox's writable roots; `path` names it. Terminal, answered before provisioning","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Templates not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream service failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Dependent service unavailable, or COMPLIANCE_CELL_UNAVAILABLE: the selected, source or recorded placement pool does not admit the caller's current compliance entitlement, or no enabled HIPAA-designated placement pool can take the create now (none configured, disabled, down or full); it was not placed in any other placement pool. Retry-After is set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"get":{"summary":"List sandboxes","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"resolveCreate","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64},"required":false,"name":"expectedCellId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"billingOwnerId","in":"query"},{"schema":{"anyOf":[{"type":"string","enum":["pending","provisioning","running","stopped","failed","expired","deleted"]},{"type":"array","items":{"type":"string","enum":["pending","provisioning","running","stopped","failed","expired","deleted"]},"minItems":1}]},"required":false,"name":"status","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":1000},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","pattern":"^(personal|team|team:team_[a-f0-9]{32}|all)$"},"required":false,"name":"scope","in":"query"},{"schema":{"type":"string","pattern":"^(?!\\.+$)[A-Za-z0-9._:-]{1,200}$"},"required":false,"name":"run","in":"query"}],"responses":{"200":{"description":"List of sandboxes","content":{"application/json":{"schema":{"type":"object","properties":{"sandboxes":{"type":"array","items":{"$ref":"#/components/schemas/SandboxResponse"}},"listingComplete":{"type":"boolean"},"createResolution":{"type":"object","properties":{"schema":{"type":"string","enum":["sandbox-create-resolution.v1"]},"run":{"type":"string"},"idempotencyKeyHash":{"type":"string"},"billingOwnerId":{"type":"string"},"cellId":{"type":"string"},"observedAt":{"type":"string"},"state":{"type":"string","enum":["resource","retired","pending","unresolved"]},"canonicalRef":{"type":"string"},"sandboxId":{"type":"string"},"ownerConfirmed":{"type":"boolean","enum":[true]},"retirement":{"type":"object","properties":{"confirmed":{"type":"boolean","enum":[true]},"fenceId":{"type":"string","format":"uuid"},"retiredAt":{"type":"string"}},"required":["confirmed","fenceId","retiredAt"]}},"required":["schema","run","idempotencyKeyHash","billingOwnerId","cellId","observedAt","state"]}},"required":["sandboxes","listingComplete"]}}}},"400":{"description":"Invalid create-resolution identity or scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Sandbox credential cannot list account-wide sandboxes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"A sandbox backend or create-resolution billing authority did not answer; retry","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["UNAVAILABLE"]},"message":{"type":"string"},"listingComplete":{"type":"boolean","enum":[false]}},"required":["error","message","listingComplete"]},{"$ref":"#/components/schemas/ErrorResponse"}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/sandboxes`, baseUrl);\nconst resolveCreate = process.env.SANDBOX_RESOLVE_CREATE;\nif (resolveCreate !== undefined) url.searchParams.set(\"resolveCreate\", resolveCreate);\nconst expectedCellId = process.env.SANDBOX_EXPECTED_CELL_ID;\nif (expectedCellId !== undefined) url.searchParams.set(\"expectedCellId\", expectedCellId);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst status = process.env.SANDBOX_STATUS;\nif (status !== undefined) url.searchParams.set(\"status\", status);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst scope = process.env.SANDBOX_SCOPE;\nif (scope !== undefined) url.searchParams.set(\"scope\", scope);\nconst run = process.env.SANDBOX_RUN;\nif (run !== undefined) url.searchParams.set(\"run\", run);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes`, baseUrl);\nconst resolveCreate = process.env.SANDBOX_RESOLVE_CREATE;\nif (resolveCreate !== undefined) url.searchParams.set(\"resolveCreate\", resolveCreate);\nconst expectedCellId = process.env.SANDBOX_EXPECTED_CELL_ID;\nif (expectedCellId !== undefined) url.searchParams.set(\"expectedCellId\", expectedCellId);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst status = process.env.SANDBOX_STATUS;\nif (status !== undefined) url.searchParams.set(\"status\", status);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst scope = process.env.SANDBOX_SCOPE;\nif (scope !== undefined) url.searchParams.set(\"scope\", scope);\nconst run = process.env.SANDBOX_RUN;\nif (run !== undefined) url.searchParams.set(\"run\", run);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes\"\nquery = {}\n_resolveCreate = os.environ.get(\"SANDBOX_RESOLVE_CREATE\")\nif _resolveCreate is not None: query[\"resolveCreate\"] = _resolveCreate\n_expectedCellId = os.environ.get(\"SANDBOX_EXPECTED_CELL_ID\")\nif _expectedCellId is not None: query[\"expectedCellId\"] = _expectedCellId\n_billingOwnerId = os.environ.get(\"SANDBOX_BILLING_OWNER_ID\")\nif _billingOwnerId is not None: query[\"billingOwnerId\"] = _billingOwnerId\n_status = os.environ.get(\"SANDBOX_STATUS\")\nif _status is not None: query[\"status\"] = _status\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_offset = os.environ.get(\"SANDBOX_OFFSET\")\nif _offset is not None: query[\"offset\"] = _offset\n_scope = os.environ.get(\"SANDBOX_SCOPE\")\nif _scope is not None: query[\"scope\"] = _scope\n_run = os.environ.get(\"SANDBOX_RUN\")\nif _run is not None: query[\"run\"] = _run\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_RESOLVE_CREATE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}resolveCreate=${SANDBOX_RESOLVE_CREATE}\"\nfi\nif [ -n \"${SANDBOX_EXPECTED_CELL_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}expectedCellId=${SANDBOX_EXPECTED_CELL_ID}\"\nfi\nif [ -n \"${SANDBOX_BILLING_OWNER_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}billingOwnerId=${SANDBOX_BILLING_OWNER_ID}\"\nfi\nif [ -n \"${SANDBOX_STATUS:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}status=${SANDBOX_STATUS}\"\nfi\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_OFFSET:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}offset=${SANDBOX_OFFSET}\"\nfi\nif [ -n \"${SANDBOX_SCOPE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}scope=${SANDBOX_SCOPE}\"\nfi\nif [ -n \"${SANDBOX_RUN:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}run=${SANDBOX_RUN}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/admission":{"get":{"tags":["Sandboxes"],"summary":"Check whether the default placement pool can admit a new sandbox","parameters":[{"schema":{"type":"string","enum":["docker","firecracker"]},"required":false,"name":"runtime","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"room","in":"query"},{"schema":{"type":"string","enum":["docker","firecracker"],"deprecated":true,"description":"Deprecated on 2026-11-05 and removed from current SDK releases: send `runtime` and omit `driver`."},"required":false,"description":"Deprecated on 2026-11-05 and removed from current SDK releases: send `runtime` and omit `driver`.","name":"runtimeBackend","in":"query"},{"schema":{"type":"string"},"required":false,"name":"billingOwnerId","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"routing","in":"query"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"required":false,"name":"ownerId","in":"query"}],"responses":{"200":{"description":"Read-only admission snapshot; a ready result does not reserve a host","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"ready":{"type":"boolean"},"available":{"type":"integer","minimum":0},"observedAt":{"type":"string"},"cellId":{"type":"string"},"billingOwnerId":{"type":"string"},"cpuBrakePercent":{"type":"number","exclusiveMinimum":0},"runtimeAvailable":{"type":"boolean"},"reason":{"type":"string","enum":["READY","LEDGER_UNCERTIFIED","NO_HOSTS","HOST_PRESSURE","NO_CAPACITY","NO_PLACEABLE_HOST","STATE_UNAVAILABLE","SUBSTRATE_FAILURE_STREAK","SUBSTRATE_PROBE_ONLY","UPSTREAM_UNAVAILABLE","COMPLIANCE_CELL_UNAVAILABLE"]}},"required":["ready","reason"]},{"type":"object","properties":{"cellId":{"type":"string"},"billingOwnerId":{"type":"string"},"observedAt":{"type":"string"}},"required":["cellId","billingOwnerId","observedAt"]},{"type":"object","properties":{"cellId":{"type":"string"},"billingOwnerId":{"type":"string"},"observedAt":{"type":"string"},"ownerId":{"type":"string"},"billingEligibility":{"type":"object","properties":{"mayBill":{"type":["boolean","null"]},"complete":{"type":"boolean"},"reason":{"type":"string","enum":["SELF_BILLING","BILLING_DELEGATION_KEY_REQUIRED"]}},"required":["mayBill","complete","reason"]},"routing":{"type":"object","properties":{"scope":{"type":"string","enum":["owner","request"]},"complete":{"type":"boolean"},"reason":{"type":"string"}},"required":["scope","complete","reason"]}},"required":["cellId","billingOwnerId","observedAt","ownerId","billingEligibility","routing"]}]}}}},"403":{"description":"Sandbox-bound credentials cannot inspect placement pool admission, or the caller may not bill the named billingOwnerId","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"observedAt":{"type":"string"}},"required":["error","code"]}}}},"503":{"description":"Admission state could not be checked","content":{"application/json":{"schema":{"type":"object","properties":{"ready":{"type":"boolean"},"available":{"type":"integer","minimum":0},"observedAt":{"type":"string"},"cellId":{"type":"string"},"billingOwnerId":{"type":"string"},"cpuBrakePercent":{"type":"number","exclusiveMinimum":0},"runtimeAvailable":{"type":"boolean"},"reason":{"type":"string","enum":["READY","LEDGER_UNCERTIFIED","NO_HOSTS","HOST_PRESSURE","NO_CAPACITY","NO_PLACEABLE_HOST","STATE_UNAVAILABLE","SUBSTRATE_FAILURE_STREAK","SUBSTRATE_PROBE_ONLY","UPSTREAM_UNAVAILABLE","COMPLIANCE_CELL_UNAVAILABLE"]}},"required":["ready","reason"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/sandboxes/admission`, baseUrl);\nconst runtime = process.env.SANDBOX_RUNTIME;\nif (runtime !== undefined) url.searchParams.set(\"runtime\", runtime);\nconst room = process.env.SANDBOX_ROOM;\nif (room !== undefined) url.searchParams.set(\"room\", room);\nconst runtimeBackend = process.env.SANDBOX_RUNTIME_BACKEND;\nif (runtimeBackend !== undefined) url.searchParams.set(\"runtimeBackend\", runtimeBackend);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst routing = process.env.SANDBOX_ROUTING;\nif (routing !== undefined) url.searchParams.set(\"routing\", routing);\nconst ownerId = process.env.SANDBOX_OWNER_ID;\nif (ownerId !== undefined) url.searchParams.set(\"ownerId\", ownerId);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/admission`, baseUrl);\nconst runtime = process.env.SANDBOX_RUNTIME;\nif (runtime !== undefined) url.searchParams.set(\"runtime\", runtime);\nconst room = process.env.SANDBOX_ROOM;\nif (room !== undefined) url.searchParams.set(\"room\", room);\nconst runtimeBackend = process.env.SANDBOX_RUNTIME_BACKEND;\nif (runtimeBackend !== undefined) url.searchParams.set(\"runtimeBackend\", runtimeBackend);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst routing = process.env.SANDBOX_ROUTING;\nif (routing !== undefined) url.searchParams.set(\"routing\", routing);\nconst ownerId = process.env.SANDBOX_OWNER_ID;\nif (ownerId !== undefined) url.searchParams.set(\"ownerId\", ownerId);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes/admission\"\nquery = {}\n_runtime = os.environ.get(\"SANDBOX_RUNTIME\")\nif _runtime is not None: query[\"runtime\"] = _runtime\n_room = os.environ.get(\"SANDBOX_ROOM\")\nif _room is not None: query[\"room\"] = _room\n_runtimeBackend = os.environ.get(\"SANDBOX_RUNTIME_BACKEND\")\nif _runtimeBackend is not None: query[\"runtimeBackend\"] = _runtimeBackend\n_billingOwnerId = os.environ.get(\"SANDBOX_BILLING_OWNER_ID\")\nif _billingOwnerId is not None: query[\"billingOwnerId\"] = _billingOwnerId\n_routing = os.environ.get(\"SANDBOX_ROUTING\")\nif _routing is not None: query[\"routing\"] = _routing\n_ownerId = os.environ.get(\"SANDBOX_OWNER_ID\")\nif _ownerId is not None: query[\"ownerId\"] = _ownerId\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/admission\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_RUNTIME:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}runtime=${SANDBOX_RUNTIME}\"\nfi\nif [ -n \"${SANDBOX_ROOM:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}room=${SANDBOX_ROOM}\"\nfi\nif [ -n \"${SANDBOX_RUNTIME_BACKEND:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}runtimeBackend=${SANDBOX_RUNTIME_BACKEND}\"\nfi\nif [ -n \"${SANDBOX_BILLING_OWNER_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}billingOwnerId=${SANDBOX_BILLING_OWNER_ID}\"\nfi\nif [ -n \"${SANDBOX_ROUTING:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}routing=${SANDBOX_ROUTING}\"\nfi\nif [ -n \"${SANDBOX_OWNER_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}ownerId=${SANDBOX_OWNER_ID}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/batch/run":{"post":{"summary":"Run multiple tasks in parallel","description":"Without Idempotency-Key, disconnecting stops the batch. With a key, the exact-body run continues and later matching requests join or replay it. Persistent reuseKey selects stable workspaces independently.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Opt-in retry identity. The keyed batch continues after disconnect; matching retries join or replay it."},"required":false,"description":"Opt-in retry identity. The keyed batch continues after disconnect; matching retries join or replay it.","name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"},"message":{"type":"string","minLength":1,"maxLength":65536},"context":{"type":"object","additionalProperties":{}},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":86400000}},"required":["id","message"],"additionalProperties":false},"minItems":1,"maxItems":1000},"backends":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"},"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1,"maxLength":65536},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0,"maximum":1000000},"mode":{"type":"string","enum":["api","cli"]}},"additionalProperties":false},"server":{"type":"object","properties":{"hostname":{"type":"string","minLength":1,"maxLength":256},"port":{"type":"integer","minimum":1,"maximum":65535}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}},"extensions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}},{}]}}},"additionalProperties":false}},"required":["id","type"],"additionalProperties":false}},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":86400000},"executionClass":{"type":"string","enum":["project","simulation","background"],"default":"simulation"},"persistent":{"type":"boolean","enum":[false],"default":false},"graceMs":{"type":"integer","minimum":0,"maximum":300000}},"required":["tasks","backends"],"additionalProperties":false},{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"},"message":{"type":"string","minLength":1,"maxLength":65536},"context":{"type":"object","additionalProperties":{}},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":86400000}},"required":["id","message"],"additionalProperties":false},"minItems":1,"maxItems":1000},"backends":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"},"type":{"type":"string","enum":["opencode","claude-code","kimi-code","codex","amp","factory-droids","pi","prime","hermes","openclaw","nanoclaw","acp","cursor","grok","cli-base"]},"model":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"maxLength":128},"model":{"type":"string","minLength":1,"maxLength":256},"apiKey":{"type":"string","minLength":1,"maxLength":65536},"baseUrl":{"type":"string","maxLength":2048,"format":"uri"},"maxThinkingTokens":{"type":"integer","minimum":0,"maximum":1000000},"mode":{"type":"string","enum":["api","cli"]}},"additionalProperties":false},"server":{"type":"object","properties":{"hostname":{"type":"string","minLength":1,"maxLength":256},"port":{"type":"integer","minimum":1,"maximum":65535}},"additionalProperties":false},"profile":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"maxItems":1024,"type":"array","items":{"type":"string"}},"prompt":{"type":"object","properties":{"systemPrompt":{"type":"string"},"appendSystemPrompt":{"type":"string"},"instructions":{"maxItems":1024,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"model":{"type":"object","properties":{"default":{"type":"string"},"small":{"type":"string"},"provider":{"type":"string"},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"maxVisibleOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxReasoningTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"maxTotalOutputTokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false},"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"seats":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","nanoclaw","codex","opencode","kimi-code","pi","prime","gemini","hermes","openclaw","amp","factory-droids","forge","cursor","grok","acp","cli-base"]},"provider":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"model":{"type":"string","minLength":1},"reasoningEffort":{"type":"string","enum":["none","minimal","low","medium","high","xhigh","ultracode"]},"credentialSource":{"type":"string","enum":["router","api-key","subscription","managed"]},"selector":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"all-eligible"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"seat"},"id":{"type":"string","minLength":1}},"required":["kind","id"],"additionalProperties":false}]},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}}},"required":["harness","provider","model","selector"],"additionalProperties":false}},"proxySecrets":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"hosts":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"}},"methods":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"}},"paths":{"readOnly":true,"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","maxLength":2048}},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"bearer"}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"basic"},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"header"},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"query"},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"mcp":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"object","properties":{"transport":{"type":"string","const":"stdio"},"command":{"type":"string"},"args":{"maxItems":1024,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"cwd":{"type":"string"},"url":{},"headers":{},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["command"],"additionalProperties":false},{"type":"object","properties":{"transport":{"type":"string","enum":["sse","http"]},"command":{},"args":{},"env":{},"cwd":{},"url":{"type":"string"},"headers":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}},"enabled":{"type":"boolean","const":true},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["url"],"additionalProperties":false},{"type":"object","properties":{"enabled":{"type":"boolean","const":false},"transport":{},"command":{},"args":{},"env":{},"cwd":{},"url":{},"headers":{},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"required":["enabled"],"additionalProperties":false}]}},"connections":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"connectionId":{"type":"string","minLength":1},"capabilities":{"minItems":1,"maxItems":1024,"type":"array","items":{"type":"string","minLength":1}},"alias":{"type":"string","minLength":1}},"required":["connectionId","capabilities"],"additionalProperties":false}},"subagents":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"prompt":{"type":"string"},"model":{"type":"string"},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"maxSteps":{"type":"number"},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false}},"resources":{"type":"object","properties":{"knowledge":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"},"generation":{"type":"string","pattern":"^kg_[a-f0-9]{64}$"}},"required":["sourceId","generation"],"additionalProperties":false}},"files":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"resource":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]},"executable":{"type":"boolean"}},"required":["path","resource"],"additionalProperties":false}},"tools":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"skills":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"agents":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"commands":{"maxItems":1024,"type":"array","items":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}},"instructions":{"anyOf":[{"type":"string"},{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"inline"},"name":{"type":"string"},"content":{"type":"string"}},"required":["kind","name","content"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"github"},"repository":{"type":"string"},"path":{"type":"string"},"ref":{"type":"string"},"name":{"type":"string"}},"required":["kind","path"],"additionalProperties":false}]}]},"failOnError":{"type":"boolean"}},"additionalProperties":false},"hooks":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"timeoutMs":{"type":"number","exclusiveMinimum":0},"blocking":{"type":"boolean"},"matcher":{"type":"string"},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"public"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret-ref"},"key":{"type":"string","minLength":1,"maxLength":500},"format":{"type":"string","enum":["raw","bearer"]}},"required":["kind","key"],"additionalProperties":false}]}}},"required":["command"],"additionalProperties":false}}},"modes":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"object","properties":{"description":{"type":"string"},"model":{"type":"string"},"prompt":{"type":"string"},"tools":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"boolean"}},"permissions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"string","enum":["allow","deny","ask"]},{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"type":"string","enum":["allow","deny","ask"]}}]}},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}}},"additionalProperties":false}},"confidential":{"type":"object","properties":{"tee":{"type":"string"},"attestationNonce":{"type":"string"},"sealed":{"type":"boolean"},"attestationRefresh":{"type":"boolean"}},"additionalProperties":false},"metadata":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}},"extensions":{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{"anyOf":[{"type":"object","propertyNames":{"type":"string","pattern":"^u(?:[0-9a-f]{4})*$"},"additionalProperties":{}},{}]}}},"additionalProperties":false}},"required":["id","type"],"additionalProperties":false}},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":86400000},"executionClass":{"type":"string","enum":["project","simulation","background"],"default":"simulation"},"persistent":{"type":"boolean","enum":[true]},"reuseKey":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[\\x21-\\x7e]+$"},"lifecycle":{"type":"object","properties":{"idleTimeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":2592000000,"default":300000},"coldTimeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":2592000000,"default":3600000}},"additionalProperties":false},"git":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"ref":{"type":"string","minLength":1,"maxLength":1024},"depth":{"type":"integer","exclusiveMinimum":0,"maximum":1000},"sparse":{"type":"array","items":{"type":"string","minLength":1,"maxLength":4096},"maxItems":256},"auth":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":4096}},"additionalProperties":false}},"required":["url"],"additionalProperties":false}},"required":["tasks","backends","persistent","reuseKey"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"SSE stream of batch progress","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Invalid batch request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"401":{"description":"Authentication required or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"403":{"description":"Caller is not allowed to run the batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"409":{"description":"Retry identity or persistent workspace state conflicts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"413":{"description":"Batch request payload is too large","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string","enum":["PAYLOAD_TOO_LARGE"]},"message":{"type":"string"},"surface":{"type":"string"},"limitBytes":{"type":"integer","exclusiveMinimum":0},"actualBytes":{"type":"integer","exclusiveMinimum":0}},"required":["code","message","surface","limitBytes"]}},"required":["success","error"]}}}},"429":{"description":"Batch rate, concurrency, or capacity limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"500":{"description":"Batch execution failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"502":{"description":"Sandbox service request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"503":{"description":"Batch service or compute capacity is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}},"504":{"description":"Sandbox service request timed out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/batch/run`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/batch/run`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes/batch/run\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/batch/run\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]}},"/v1/sandboxes/delete-matching":{"post":{"summary":"Delete every sandbox whose metadata matches","description":"Deletes the caller's own sandboxes whose metadata values start with the given prefixes, at most 25 per call in ID order, and reports each one. When matches remain, pass `next` as `after` to attempt them. Repeating the call is safe: a confirmed-gone sandbox no longer matches. Stop creating matching sandboxes before calling, and call again once in-flight creates have settled: `complete` covers only the sandboxes this call listed.","tags":["Sandboxes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteMatchingSandboxesRequest"}}}},"responses":{"200":{"description":"Per-sandbox delete results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteMatchingSandboxesResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Sandbox credential cannot select account-wide deletes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/delete-matching`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/delete-matching`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes/delete-matching\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/delete-matching\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/runs/{run}":{"get":{"tags":["Sandboxes"],"summary":"List the sandbox compute generations created for a caller run","parameters":[{"schema":{"type":"string","pattern":"^(?!\\.+$)[A-Za-z0-9._:-]{1,200}$"},"required":true,"name":"run","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"description":"Generations per page (default 1000)."},"required":false,"description":"Generations per page (default 1000).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":1024,"description":"The `nextCursor` of the previous page."},"required":false,"description":"The `nextCursor` of the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Generations created for the run, oldest first","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"type":"string"},"generations":{"type":"array","items":{"type":"object","properties":{"sandboxId":{"type":"string"},"generation":{"type":"string"},"generationSeq":{"type":"number"},"orgId":{"type":"string"},"state":{"type":"string"},"startedAt":{"type":"number"},"closedAt":{"type":["number","null"]},"closedReason":{"type":["string","null"]}},"required":["sandboxId","generation","generationSeq","orgId","state","startedAt","closedAt","closedReason"]}},"nextCursor":{"type":["string","null"]}},"required":["run","generations","nextCursor"]}}}},"400":{"description":"The cursor is malformed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Sandbox credential cannot read account-wide sandboxes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"This runtime does not index generations by caller run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst run = process.env.SANDBOX_RUN;\nif (!run) throw new Error(\"Set SANDBOX_RUN\");\nconst url = new URL(`/v1/sandboxes/runs/${encodeURIComponent(run)}`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst cursor = process.env.SANDBOX_CURSOR;\nif (cursor !== undefined) url.searchParams.set(\"cursor\", cursor);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst run = process.env.SANDBOX_RUN;\nif (!run) throw new Error(\"Set SANDBOX_RUN\");\nconst url = new URL(`/v1/sandboxes/runs/${encodeURIComponent(run)}`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst cursor = process.env.SANDBOX_CURSOR;\nif (cursor !== undefined) url.searchParams.set(\"cursor\", cursor);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_run = os.environ.get(\"SANDBOX_RUN\")\nif not _run: raise SystemExit(\"Set SANDBOX_RUN\")\nurl = f\"{base_url}/v1/sandboxes/runs/{quote(_run or '', safe='')}\"\nquery = {}\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_cursor = os.environ.get(\"SANDBOX_CURSOR\")\nif _cursor is not None: query[\"cursor\"] = _cursor\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/runs/${SANDBOX_RUN:?Set SANDBOX_RUN}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_CURSOR:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}cursor=${SANDBOX_CURSOR}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/self/identity":{"get":{"tags":["Sandboxes"],"summary":"Get the ID of the calling sandbox","description":"Needs a credential bound to one sandbox. An account API key gets 403: select the sandbox by ID instead. The response is not cached.","responses":{"200":{"description":"ID of the sandbox the credential is bound to","content":{"application/json":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string"}},"required":["sandboxId"]}}}},"401":{"description":"No authenticated caller (UNAUTHORIZED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential is not bound to a sandbox (SANDBOX_SELF_UNBOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/sandboxes/self/identity`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sandboxes/self/identity`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sandboxes/self/identity\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/self/identity\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}":{"get":{"summary":"Get sandbox details","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"Sandbox details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"summary":"Delete a sandbox","description":"Deletes the sandbox. A sandbox with live branch children is refused (409 CHILDREN_EXIST) unless `cascade=true`, which deletes the whole lineage children-first. Platform-managed snapshot repositories are reclaimed after the deleted-retention window for internal accounts only; customer repositories remain until explicitly deleted by their owner. Preservation holds and active exports/restores are honored. With `wait=false` it may instead answer 202 as soon as the delete is durably accepted: poll `operation.statusUrl` (or `Location`) until the operation is `succeeded` (with `result.deleted`) or `failed`. The runtime runs and bills until the delete kills it. An `Idempotency-Key` names the delete for a retry whose 202 was lost. With `until=removed`, that status URL keeps polling through physical reclaim. Cascades keep the synchronous answer; a 200 means the delete finished.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"retireCreate","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"run","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64},"required":false,"name":"expectedCellId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"billingOwnerId","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"cascade","in":"query"},{"schema":{"type":"string","enum":["removed"]},"required":false,"name":"until","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"wait","in":"query"}],"responses":{"200":{"description":"Sandbox deleted, or already absent","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"createResolution":{"type":"object","properties":{"schema":{"type":"string","enum":["sandbox-create-resolution.v1"]},"run":{"type":"string"},"idempotencyKeyHash":{"type":"string"},"billingOwnerId":{"type":"string"},"cellId":{"type":"string"},"observedAt":{"type":"string"},"state":{"type":"string","enum":["resource","retired","pending","unresolved"]},"canonicalRef":{"type":"string"},"sandboxId":{"type":"string"},"ownerConfirmed":{"type":"boolean","enum":[true]},"retirement":{"type":"object","properties":{"confirmed":{"type":"boolean","enum":[true]},"fenceId":{"type":"string","format":"uuid"},"retiredAt":{"type":"string"}},"required":["confirmed","fenceId","retiredAt"]}},"required":["schema","run","idempotencyKeyHash","billingOwnerId","cellId","observedAt","state"]}},"required":["createResolution"]},{"type":"object","properties":{"success":{"type":"boolean"},"outcome":{"type":"string","enum":["destroyed","already_absent"]},"removal":{"type":"string","enum":["pending","complete"]},"deletedChildren":{"type":"array","items":{"type":"string"}},"settlementDeferred":{"type":"boolean"}},"required":["success","outcome"]}]}}}},"202":{"description":"Delete accepted (`wait=false`); it finishes in the background. Or create retirement is pending","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SandboxOperationAccepted"},{"type":"object","properties":{"createResolution":{"type":"object","properties":{"schema":{"type":"string","enum":["sandbox-create-resolution.v1"]},"run":{"type":"string"},"idempotencyKeyHash":{"type":"string"},"billingOwnerId":{"type":"string"},"cellId":{"type":"string"},"observedAt":{"type":"string"},"state":{"type":"string","enum":["resource","retired","pending","unresolved"]},"canonicalRef":{"type":"string"},"sandboxId":{"type":"string"},"ownerConfirmed":{"type":"boolean","enum":[true]},"retirement":{"type":"object","properties":{"confirmed":{"type":"boolean","enum":[true]},"fenceId":{"type":"string","format":"uuid"},"retiredAt":{"type":"string"}},"required":["confirmed","fenceId","retiredAt"]}},"required":["schema","run","idempotencyKeyHash","billingOwnerId","cellId","observedAt","state"]}},"required":["createResolution"]}]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Sandbox has live branch children, lacks a confirmed final snapshot, could not prove absence, is under an operator retention hold, or its Idempotency-Key names a different delete","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["CHILDREN_EXIST"]},"children":{"type":"array","items":{"type":"string"}},"computeStopped":{"type":"boolean"},"computeStopRefusal":{"type":"string"}},"required":["error","code","children"]},{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["SANDBOX_DELETE_NOT_CONFIRMED"]}},"required":["error","code"]},{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["FINAL_SUSPEND_UNCONFIRMED"]},"blockingPrerequisite":{"type":"string","enum":["snapshot_confirmed"]}},"required":["error","code","blockingPrerequisite"]},{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["IDEMPOTENCY_KEY_REUSED"]}},"required":["error","code"]},{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["RETENTION_HOLD"]},"holdId":{"type":"string"},"rule":{"type":["string","null"]},"reason":{"type":"string"},"expiresAt":{"type":["string","null"]},"computeStopped":{"type":"boolean"},"computeStopRefusal":{"type":"string"}},"required":["error","code","holdId","rule","reason","expiresAt"]}]}}}},"500":{"description":"Delete failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Sandbox billing state is temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}`, baseUrl);\nconst retireCreate = process.env.SANDBOX_RETIRE_CREATE;\nif (retireCreate !== undefined) url.searchParams.set(\"retireCreate\", retireCreate);\nconst run = process.env.SANDBOX_RUN;\nif (run !== undefined) url.searchParams.set(\"run\", run);\nconst expectedCellId = process.env.SANDBOX_EXPECTED_CELL_ID;\nif (expectedCellId !== undefined) url.searchParams.set(\"expectedCellId\", expectedCellId);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst cascade = process.env.SANDBOX_CASCADE;\nif (cascade !== undefined) url.searchParams.set(\"cascade\", cascade);\nconst until = process.env.SANDBOX_UNTIL;\nif (until !== undefined) url.searchParams.set(\"until\", until);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}`, baseUrl);\nconst retireCreate = process.env.SANDBOX_RETIRE_CREATE;\nif (retireCreate !== undefined) url.searchParams.set(\"retireCreate\", retireCreate);\nconst run = process.env.SANDBOX_RUN;\nif (run !== undefined) url.searchParams.set(\"run\", run);\nconst expectedCellId = process.env.SANDBOX_EXPECTED_CELL_ID;\nif (expectedCellId !== undefined) url.searchParams.set(\"expectedCellId\", expectedCellId);\nconst billingOwnerId = process.env.SANDBOX_BILLING_OWNER_ID;\nif (billingOwnerId !== undefined) url.searchParams.set(\"billingOwnerId\", billingOwnerId);\nconst cascade = process.env.SANDBOX_CASCADE;\nif (cascade !== undefined) url.searchParams.set(\"cascade\", cascade);\nconst until = process.env.SANDBOX_UNTIL;\nif (until !== undefined) url.searchParams.set(\"until\", until);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}\"\nquery = {}\n_retireCreate = os.environ.get(\"SANDBOX_RETIRE_CREATE\")\nif _retireCreate is not None: query[\"retireCreate\"] = _retireCreate\n_run = os.environ.get(\"SANDBOX_RUN\")\nif _run is not None: query[\"run\"] = _run\n_expectedCellId = os.environ.get(\"SANDBOX_EXPECTED_CELL_ID\")\nif _expectedCellId is not None: query[\"expectedCellId\"] = _expectedCellId\n_billingOwnerId = os.environ.get(\"SANDBOX_BILLING_OWNER_ID\")\nif _billingOwnerId is not None: query[\"billingOwnerId\"] = _billingOwnerId\n_cascade = os.environ.get(\"SANDBOX_CASCADE\")\nif _cascade is not None: query[\"cascade\"] = _cascade\n_until = os.environ.get(\"SANDBOX_UNTIL\")\nif _until is not None: query[\"until\"] = _until\n_wait = os.environ.get(\"SANDBOX_WAIT\")\nif _wait is not None: query[\"wait\"] = _wait\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_RETIRE_CREATE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}retireCreate=${SANDBOX_RETIRE_CREATE}\"\nfi\nif [ -n \"${SANDBOX_RUN:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}run=${SANDBOX_RUN}\"\nfi\nif [ -n \"${SANDBOX_EXPECTED_CELL_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}expectedCellId=${SANDBOX_EXPECTED_CELL_ID}\"\nfi\nif [ -n \"${SANDBOX_BILLING_OWNER_ID:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}billingOwnerId=${SANDBOX_BILLING_OWNER_ID}\"\nfi\nif [ -n \"${SANDBOX_CASCADE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}cascade=${SANDBOX_CASCADE}\"\nfi\nif [ -n \"${SANDBOX_UNTIL:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}until=${SANDBOX_UNTIL}\"\nfi\nif [ -n \"${SANDBOX_WAIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}wait=${SANDBOX_WAIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/branch":{"post":{"summary":"Branch a running sandbox into copy-on-write children","description":"Forks a RUNNING sandbox's live memory into `count` copy-on-write children that share the parent's pages at branch time (Firecracker UFFD copy-on-write). The parent stays running. Fails loud when the active driver cannot branch live memory; it never degrades to fresh, empty sandboxes.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."},"required":false,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation.","name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":1,"maximum":16,"default":1},"env":{"type":"object","additionalProperties":{"type":"string"}},"metadata":{"type":"object","additionalProperties":{}},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Children materialized, or the stored result of an earlier branch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BranchResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Another request holds this idempotency key, or the key is bound to a different request body","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/WorkspaceOperationConflict"}]}}}},"429":{"description":"Concurrent session limit exceeded, or the operation ledger is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Active driver does not support live branching","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/branch`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/branch`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/branch\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/branch\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]}},"/v1/sandboxes/{id}/driver":{"get":{"summary":"Get sandbox driver info","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"Driver info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriverInfo"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/driver`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/driver`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/driver\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/driver\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/egress":{"patch":{"summary":"Update sandbox egress policy","description":"Update the egress (outbound network) policy for a running sandbox. The proxy is restarted with the new policy (~2-5s downtime).","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Policy updated","content":{"application/json":{"schema":{"type":"object","properties":{"egressPolicy":{"type":"object","properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false},"source":{"type":"string","enum":["sandbox","team","platform"]}},"required":["egressPolicy","source"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Driver does not support runtime policy updates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/egress\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/egress\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/egress/activity":{"get":{"tags":["Sandboxes"],"summary":"List the hosts a sandbox reached","description":"Every destination this sandbox's egress proxy allowed or refused in the inspected window, and whether that observation can be trusted. This is the OPPOSITE question to /egress/denials, and unlike that route it is deliberately not best-effort: it exists to support the negative claim 'this sandbox reached nothing', which an empty list cannot carry on its own. `destinations: []` with `complete: true` is evidence; `destinations: []` with `complete: false` is an absence of evidence, and `incompleteReasons` names what was missing. A hop that could not reach the host reports `hop-unreachable` rather than an empty list, so an unreadable path can never be read as a clean sandbox.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":500},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"What the sandbox reached, and whether that is decidable","content":{"application/json":{"schema":{"type":"object","properties":{"activity":{"type":"object","properties":{"destinations":{"type":"array","items":{"type":"object","properties":{"host":{"type":"string"},"action":{"type":"string","enum":["allow","reject"]},"count":{"type":"number"},"firstAt":{"type":"string"},"lastAt":{"type":"string"},"rejectedBy":{"type":"string"}},"required":["host","action","count"]}},"requestsObserved":{"type":"number"},"unparsedLines":{"type":"number"},"complete":{"type":"boolean"},"incompleteReasons":{"type":"array","items":{"type":"string","enum":["window-truncated","unparsed-lines","host-limit-reached","log-unreadable","hop-unreachable"]}}},"required":["destinations","requestsObserved","unparsedLines","complete","incompleteReasons"]}},"required":["activity"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Egress is not enabled for this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/activity`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/activity`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/egress/activity\"\nquery = {}\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/egress/activity\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/egress/denials":{"get":{"tags":["Sandboxes"],"summary":"List recent egress denials","description":"Destinations this sandbox tried to reach that its egress policy refused, newest first. The sandbox's 403 body names each refusal's host and policy (the same pair as `rejectedBy`); this keeps the history. Best-effort: an empty list means either nothing was refused or the proxy is no longer readable.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"integer","exclusiveMinimum":0,"maximum":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Refused destinations","content":{"application/json":{"schema":{"type":"object","properties":{"denials":{"type":"array","items":{"type":"object","properties":{"host":{"type":"string"},"rejectedBy":{"type":"string"},"count":{"type":"number"},"lastAt":{"type":"string"}},"required":["host","rejectedBy","count"]}}},"required":["denials"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/denials`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/denials`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/egress/denials\"\nquery = {}\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/egress/denials\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/egress/usage":{"get":{"summary":"Get a sandbox's egress for a calendar month","description":"Bytes this sandbox sent in one calendar month (UTC), the account's allowance it shares (2 TB per account per month on paid plans, 100 GiB on Free), the account's total and overage, and the USD the meter charged or reserved for this sandbox's captures.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Calendar month, `YYYY-MM` in UTC. Defaults to the current month.","example":"2026-10"},"required":false,"description":"Calendar month, `YYYY-MM` in UTC. Defaults to the current month.","name":"month","in":"query"}],"responses":{"200":{"description":"The sandbox's egress for the month","content":{"application/json":{"schema":{"type":"object","properties":{"usage":{"$ref":"#/components/schemas/SandboxEgressUsage"}},"required":["usage"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/usage`, baseUrl);\nconst month = process.env.SANDBOX_MONTH;\nif (month !== undefined) url.searchParams.set(\"month\", month);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/egress/usage`, baseUrl);\nconst month = process.env.SANDBOX_MONTH;\nif (month !== undefined) url.searchParams.set(\"month\", month);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/egress/usage\"\nquery = {}\n_month = os.environ.get(\"SANDBOX_MONTH\")\nif _month is not None: query[\"month\"] = _month\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/egress/usage\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_MONTH:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}month=${SANDBOX_MONTH}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/env-recovery":{"get":{"tags":["Sandboxes"],"summary":"Read caller credential recovery policy","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Credential recovery metadata (no values)","content":{"application/json":{"schema":{"type":"object","properties":{"envRecovery":{"type":"object","properties":{"callerResupplies":{"type":"array","items":{"type":"string","maxLength":256},"maxItems":128}},"required":["callerResupplies"],"additionalProperties":false},"eligibleCallerResupplyKeys":{"type":"array","items":{"type":"string"}},"requiredOnColdResumeKeys":{"type":"array","items":{"type":"string"}},"automaticCredentialRecovery":{"type":"boolean","enum":[false]},"dryRun":{"type":"boolean"}},"required":["envRecovery","eligibleCallerResupplyKeys","requiredOnColdResumeKeys","automaticCredentialRecovery","dryRun"],"additionalProperties":false}}}},"400":{"description":"Invalid policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Owner or team admin required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Sandbox must be stopped","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Policy unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid upstream response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/env-recovery`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/env-recovery`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/env-recovery\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/env-recovery\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"put":{"tags":["Sandboxes"],"summary":"Replace or dry-run a stopped sandbox's caller resupply policy","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"envRecovery":{"type":"object","properties":{"callerResupplies":{"type":"array","items":{"type":"string","maxLength":256},"maxItems":128}},"required":["callerResupplies"],"additionalProperties":false},"dryRun":{"type":"boolean"}},"required":["envRecovery"],"additionalProperties":false}}}},"responses":{"200":{"description":"Credential recovery metadata (no values)","content":{"application/json":{"schema":{"type":"object","properties":{"envRecovery":{"type":"object","properties":{"callerResupplies":{"type":"array","items":{"type":"string","maxLength":256},"maxItems":128}},"required":["callerResupplies"],"additionalProperties":false},"eligibleCallerResupplyKeys":{"type":"array","items":{"type":"string"}},"requiredOnColdResumeKeys":{"type":"array","items":{"type":"string"}},"automaticCredentialRecovery":{"type":"boolean","enum":[false]},"dryRun":{"type":"boolean"}},"required":["envRecovery","eligibleCallerResupplyKeys","requiredOnColdResumeKeys","automaticCredentialRecovery","dryRun"],"additionalProperties":false}}}},"400":{"description":"Invalid policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Owner or team admin required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Sandbox must be stopped","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Policy unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid upstream response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Upstream unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/env-recovery`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/env-recovery`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/env-recovery\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/env-recovery\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/events":{"get":{"summary":"Subscribe to sandbox events (SSE)","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"SSE stream of sandbox events","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/events`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/events`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/events\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/events\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/gpu/leases":{"get":{"tags":["GPU"],"summary":"List GPU leases","description":"Needs read access to the sandbox. Each lease's endpointUrl is its serving URL on this API. A destroyed lease in the list has its final charge recorded once.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"GPU leases","content":{"application/json":{"schema":{"type":"object","properties":{"leases":{"type":"array","items":{"$ref":"#/components/schemas/GpuLease"}}},"required":["leases"]}}}},"400":{"description":"The sandbox's driver cannot be routed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"The leases could not be listed (GPU_LEASE_LIST_FAILED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["GPU"],"summary":"Attach a GPU lease","description":"Needs admin access to the sandbox and a paid seat or funded balance. The plan's accelerator limit applies per sandbox. maxSpendUsd and maxLifetimeSeconds end the lease when reached.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachGpuLeaseRequest"}}}},"responses":{"201":{"description":"GPU lease attached","content":{"application/json":{"schema":{"type":"object","properties":{"lease":{"$ref":"#/components/schemas/GpuLease"}},"required":["lease"]}}}},"400":{"description":"Invalid request, the plan's accelerator limit or the spend cap refused it, or the sandbox's driver cannot be routed (INVALID_REQUEST, ACCELERATOR_LIMIT_EXCEEDED, GPU_SPEND_CAP_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The account has no paid seat or funded balance (PAYMENT_REQUIRED, INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease is not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"The paying account's balance is below the admission floor (QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"reason":{"type":"string"},"tier":{"type":"string"},"upgradeAvailable":{"type":"boolean"},"limit":{"type":"number"}}}]}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/gpu/leases/{leaseId}":{"patch":{"tags":["GPU"],"summary":"Renew a GPU lease","description":"Sets a new lifetime or spend cap. Send at least one of maxLifetimeSeconds and maxSpendUsd. Needs admin access to the sandbox and a paid seat or funded balance.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"maxSpendUsd":{"type":"number","exclusiveMinimum":0,"maximum":10000},"maxLifetimeSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":86400}},"additionalProperties":false}}}},"responses":{"200":{"description":"GPU lease renewed","content":{"application/json":{"schema":{"type":"object","properties":{"lease":{"$ref":"#/components/schemas/GpuLease"}},"required":["lease"]}}}},"400":{"description":"Invalid request, or the spend cap refused it (INVALID_REQUEST, GPU_SPEND_CAP_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The account has no paid seat or funded balance (PAYMENT_REQUIRED, INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease is not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["GPU"],"summary":"Detach a GPU lease","description":"Needs write access to the sandbox. Destroys the lease and records its final charge. The response carries the lease's billing summary.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"}],"responses":{"200":{"description":"GPU lease detached","content":{"application/json":{"schema":{"type":"object","properties":{"lease":{"$ref":"#/components/schemas/GpuLease"}},"required":["lease"]}}}},"400":{"description":"The sandbox's driver cannot be routed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease is not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/gpu/leases/{leaseId}/endpoint/{path}":{"get":{"tags":["GPU"],"summary":"Send a GET request to a GPU lease endpoint","description":"Forwards the request to the lease's HTTPS serving endpoint, with its query string, body, Content-Type and Accept. The response status, headers and body are the endpoint's. Needs write access to the sandbox. A request in flight counts as lease activity.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"},{"schema":{"type":"string","description":"Path on the serving endpoint, without a leading slash; empty for its root"},"required":true,"description":"Path on the serving endpoint, without a leading slash; empty for its root","name":"path","in":"path"}],"responses":{"200":{"description":"The serving endpoint's response, relayed with its own status, headers and body"},"400":{"description":"The path leaves the serving endpoint","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease has no serving endpoint, or its credential is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/endpoint/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/endpoint/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["GPU"],"summary":"Send a POST request to a GPU lease endpoint","description":"Forwards the request to the lease's HTTPS serving endpoint, with its query string, body, Content-Type and Accept. The response status, headers and body are the endpoint's. Needs write access to the sandbox. A request in flight counts as lease activity.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"},{"schema":{"type":"string","description":"Path on the serving endpoint, without a leading slash; empty for its root"},"required":true,"description":"Path on the serving endpoint, without a leading slash; empty for its root","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The serving endpoint's response, relayed with its own status, headers and body"},"400":{"description":"The path leaves the serving endpoint","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease has no serving endpoint, or its credential is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/endpoint/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/endpoint/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"put":{"tags":["GPU"],"summary":"Send a PUT request to a GPU lease endpoint","description":"Forwards the request to the lease's HTTPS serving endpoint, with its query string, body, Content-Type and Accept. The response status, headers and body are the endpoint's. Needs write access to the sandbox. A request in flight counts as lease activity.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"},{"schema":{"type":"string","description":"Path on the serving endpoint, without a leading slash; empty for its root"},"required":true,"description":"Path on the serving endpoint, without a leading slash; empty for its root","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The serving endpoint's response, relayed with its own status, headers and body"},"400":{"description":"The path leaves the serving endpoint","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease has no serving endpoint, or its credential is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/endpoint/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/endpoint/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"patch":{"tags":["GPU"],"summary":"Send a PATCH request to a GPU lease endpoint","description":"Forwards the request to the lease's HTTPS serving endpoint, with its query string, body, Content-Type and Accept. The response status, headers and body are the endpoint's. Needs write access to the sandbox. A request in flight counts as lease activity.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"},{"schema":{"type":"string","description":"Path on the serving endpoint, without a leading slash; empty for its root"},"required":true,"description":"Path on the serving endpoint, without a leading slash; empty for its root","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The serving endpoint's response, relayed with its own status, headers and body"},"400":{"description":"The path leaves the serving endpoint","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease has no serving endpoint, or its credential is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/endpoint/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/endpoint/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["GPU"],"summary":"Send a DELETE request to a GPU lease endpoint","description":"Forwards the request to the lease's HTTPS serving endpoint, with its query string, body, Content-Type and Accept. The response status, headers and body are the endpoint's. Needs write access to the sandbox. A request in flight counts as lease activity.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"},{"schema":{"type":"string","description":"Path on the serving endpoint, without a leading slash; empty for its root"},"required":true,"description":"Path on the serving endpoint, without a leading slash; empty for its root","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The serving endpoint's response, relayed with its own status, headers and body"},"400":{"description":"The path leaves the serving endpoint","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease has no serving endpoint, or its credential is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/endpoint/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/endpoint/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/endpoint/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/gpu/leases/{leaseId}/exec":{"post":{"tags":["GPU"],"summary":"Run a command on a GPU lease","description":"Needs write access to the sandbox. Runs the command on the lease's accelerator host and answers when it exits. The call is recorded as a sandbox exec access event.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"GPU lease ID"},"required":true,"description":"GPU lease ID","name":"leaseId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GpuLeaseExecRequest"}}}},"responses":{"200":{"description":"Command result","content":{"application/json":{"schema":{"type":"object","properties":{"lease":{"$ref":"#/components/schemas/GpuLease"},"result":{"type":"object","properties":{"exitCode":{"type":"number"},"stdout":{"type":"string"},"stderr":{"type":"string"},"durationMs":{"type":"number"}},"required":["exitCode","stdout","stderr"]}},"required":["lease","result"]}}}},"400":{"description":"Invalid request (INVALID_REQUEST), or the sandbox's driver cannot be routed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The account that pays for the sandbox has recorded debt (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox or lease not found","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"409":{"description":"The lease is not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"The paying account's balance is below the admission floor (QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"reason":{"type":"string"},"tier":{"type":"string"},"upgradeAvailable":{"type":"boolean"},"limit":{"type":"number"}}}]}}}},"500":{"description":"GPU lease operation failed","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"The GPU provider failed (GPU_PROVIDER_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"No GPU provider is configured (GPU_PROVIDER_NOT_CONFIGURED lists each provider and the variables it needs), the provider or the lease store is unavailable, or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["GPU_PROVIDER_NOT_CONFIGURED"]},"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"state":{"type":"string","enum":["ready","credential-invalid","unverified","not-configured","disabled"]},"envVars":{"type":"array","items":{"type":"string"}}},"required":["provider","state","envVars"]}}},"required":["error","code","providers"]},{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/exec`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst leaseId = process.env.SANDBOX_LEASE_ID;\nif (!leaseId) throw new Error(\"Set SANDBOX_LEASE_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/leases/${encodeURIComponent(leaseId)}/exec`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_leaseId = os.environ.get(\"SANDBOX_LEASE_ID\")\nif not _leaseId: raise SystemExit(\"Set SANDBOX_LEASE_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/leases/{quote(_leaseId or '', safe='')}/exec\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/leases/${SANDBOX_LEASE_ID:?Set SANDBOX_LEASE_ID}/exec\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/gpu/providers":{"get":{"tags":["GPU"],"summary":"List GPU providers","description":"Needs read access to the sandbox. Checks each provider's credential with a fresh read-only call. Send acceleratorKind to also get each provider's hourly price for that GPU.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-z0-9][a-z0-9._-]*$"},"required":false,"name":"acceleratorKind","in":"query"},{"schema":{"type":"number"},"required":false,"name":"acceleratorCount","in":"query"},{"schema":{"type":"number","exclusiveMinimum":0},"required":false,"name":"acceleratorVramGb","in":"query"}],"responses":{"200":{"description":"GPU provider health","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"string"},"description":"Providers a lease can use now"},"statuses":{"type":"array","items":{"$ref":"#/components/schemas/GpuProviderStatus"},"description":"Every supported provider"}},"required":["providers","statuses"]}}}},"400":{"description":"Invalid query (INVALID_REQUEST), or the sandbox's driver cannot be routed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED, DRIVER_NOT_ALLOWED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"503":{"description":"Provider discovery is unavailable (GPU_PROVIDER_UNAVAILABLE), or the sandbox service did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/providers`, baseUrl);\nconst acceleratorKind = process.env.SANDBOX_ACCELERATOR_KIND;\nif (acceleratorKind !== undefined) url.searchParams.set(\"acceleratorKind\", acceleratorKind);\nconst acceleratorCount = process.env.SANDBOX_ACCELERATOR_COUNT;\nif (acceleratorCount !== undefined) url.searchParams.set(\"acceleratorCount\", acceleratorCount);\nconst acceleratorVramGb = process.env.SANDBOX_ACCELERATOR_VRAM_GB;\nif (acceleratorVramGb !== undefined) url.searchParams.set(\"acceleratorVramGb\", acceleratorVramGb);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/gpu/providers`, baseUrl);\nconst acceleratorKind = process.env.SANDBOX_ACCELERATOR_KIND;\nif (acceleratorKind !== undefined) url.searchParams.set(\"acceleratorKind\", acceleratorKind);\nconst acceleratorCount = process.env.SANDBOX_ACCELERATOR_COUNT;\nif (acceleratorCount !== undefined) url.searchParams.set(\"acceleratorCount\", acceleratorCount);\nconst acceleratorVramGb = process.env.SANDBOX_ACCELERATOR_VRAM_GB;\nif (acceleratorVramGb !== undefined) url.searchParams.set(\"acceleratorVramGb\", acceleratorVramGb);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/gpu/providers\"\nquery = {}\n_acceleratorKind = os.environ.get(\"SANDBOX_ACCELERATOR_KIND\")\nif _acceleratorKind is not None: query[\"acceleratorKind\"] = _acceleratorKind\n_acceleratorCount = os.environ.get(\"SANDBOX_ACCELERATOR_COUNT\")\nif _acceleratorCount is not None: query[\"acceleratorCount\"] = _acceleratorCount\n_acceleratorVramGb = os.environ.get(\"SANDBOX_ACCELERATOR_VRAM_GB\")\nif _acceleratorVramGb is not None: query[\"acceleratorVramGb\"] = _acceleratorVramGb\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/gpu/providers\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_ACCELERATOR_KIND:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}acceleratorKind=${SANDBOX_ACCELERATOR_KIND}\"\nfi\nif [ -n \"${SANDBOX_ACCELERATOR_COUNT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}acceleratorCount=${SANDBOX_ACCELERATOR_COUNT}\"\nfi\nif [ -n \"${SANDBOX_ACCELERATOR_VRAM_GB:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}acceleratorVramGb=${SANDBOX_ACCELERATOR_VRAM_GB}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/images":{"post":{"tags":["Sandboxes"],"summary":"Publish the sandbox workspace as an image","description":"Builds the Dockerfile in the sandbox workspace and pushes the image to the registry named in repository, with the credentials in auth. The sandbox must be running, and filesystemIncarnationId must be the value its details report. The request can take up to 15 minutes. Send an Idempotency-Key to make a retry safe; reusing a key with different inputs fails with IDEMPOTENCY_KEY_REUSED. A key is generated when you omit one, and the response returns it.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":16,"maxLength":128,"pattern":"^[A-Za-z0-9_-]+$"},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"contextPath":{"type":"string","minLength":1,"maxLength":512,"default":"."},"dockerfilePath":{"type":"string","minLength":1,"maxLength":512,"default":"Dockerfile"},"repository":{"type":"string","minLength":1,"maxLength":512},"auth":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":256},"password":{"type":"string","minLength":1,"maxLength":16384}},"required":["username","password"],"additionalProperties":false},"platform":{"type":"string","enum":["linux/amd64","linux/arm64"]},"filesystemIncarnationId":{"type":"string","format":"uuid"}},"required":["repository","auth","filesystemIncarnationId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Image pushed","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string","minLength":1,"maxLength":1024},"taggedImage":{"type":"string","minLength":1,"maxLength":1024},"digest":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"imageId":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"sizeBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"contextBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"contextEntries":{"type":"integer","minimum":0,"maximum":9007199254740991},"buildDurationMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"pushDurationMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"exposedPorts":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"protocol":{"type":"string","enum":["tcp","udp","sctp"]}},"required":["port","protocol"],"additionalProperties":false},"maxItems":64}},"required":["image","taggedImage","digest","imageId","sizeBytes","contextBytes","contextEntries","buildDurationMs","pushDurationMs","exposedPorts"],"additionalProperties":false}}}},"400":{"description":"Invalid request or Idempotency-Key (INVALID_REQUEST, INVALID_IDEMPOTENCY_KEY), the sandbox's driver cannot be routed, or the build refused an input such as the context path or repository","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":false,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential or team role cannot use this sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found (PROJECT_NOT_FOUND)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"408":{"description":"The publication was cancelled (IMAGE_PUBLISH_CANCELLED)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The sandbox is not ready or running, its filesystem changed since filesystemIncarnationId was read, or another publication is active (SANDBOX_NOT_READY, SANDBOX_NOT_RUNNING, FILESYSTEM_TRANSITIONING, FILESYSTEM_INCARNATION_CHANGED, WORKSPACE_IMAGE_OPERATION_IN_PROGRESS, IDEMPOTENCY_KEY_REUSED)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"The registry or the image builder is throttling","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Build or push failed (IMAGE_BUILD_FAILED, IMAGE_PUBLISH_FAILED)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"The sandbox driver cannot publish images (DRIVER_CAPABILITY_MISSING)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The sandbox service returned an invalid result, or the publication failed for an unlisted reason (INVALID_IMAGE_PUBLISH_RESPONSE, IMAGE_PUBLISH_FAILED)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Publication is unavailable, or its outcome could not be confirmed; retry with the same Idempotency-Key (IMAGE_PUBLISH_UNAVAILABLE, IMAGE_PUBLISH_OUTCOME_UNKNOWN)","headers":{"Idempotency-Key":{"schema":{"type":"string","description":"The key the publication ran under"},"required":true,"description":"The key the publication ran under"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/images`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/images`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/images\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/images\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]}},"/v1/sandboxes/{id}/mcp":{"get":{"summary":"Get an MCP endpoint for this sandbox","description":"Returns a paste-able MCP host config (Claude Desktop, Cursor, claude-code, codex, …) plus a freshly-minted, capability-scoped JWT. The token is short-lived and can ONLY call MCP routes; it cannot reach admin endpoints (/exec, /files, …) on the sandbox. Call again to rotate.\n\n`?capabilities=computer_use` is also the mint route for desktop control: the resulting token is what `POST /computer-use/action`, `/a11y`, and `/mcp` require. `POST /v1/sandboxes/{id}/scoped-token` cannot grant it; that route covers session reads, files, and terminals only.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"capabilities","in":"query"},{"schema":{"type":"string"},"required":false,"name":"serverName","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":60},"required":false,"name":"ttlMinutes","in":"query"}],"responses":{"200":{"description":"MCP endpoint config + scoped token","content":{"application/json":{"schema":{"type":"object","properties":{"config":{"type":"object","properties":{"mcpServers":{"type":"object","additionalProperties":{"type":"object","properties":{"type":{"type":"string","enum":["http"]},"url":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}}},"required":["type","url","headers"]}}},"required":["mcpServers"]},"serverName":{"type":"string"},"url":{"type":"string"},"authToken":{"type":"string"},"expiresAt":{"type":"string"},"capabilities":{"type":"array","items":{"type":"string","enum":["computer_use"]}}},"required":["config","serverName","url","authToken","expiresAt","capabilities"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed or sandbox not yours","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid sandbox service response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Sandbox public edge is still attaching; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/mcp`, baseUrl);\nconst capabilities = process.env.SANDBOX_CAPABILITIES;\nif (capabilities !== undefined) url.searchParams.set(\"capabilities\", capabilities);\nconst serverName = process.env.SANDBOX_SERVER_NAME;\nif (serverName !== undefined) url.searchParams.set(\"serverName\", serverName);\nconst ttlMinutes = process.env.SANDBOX_TTL_MINUTES;\nif (ttlMinutes !== undefined) url.searchParams.set(\"ttlMinutes\", ttlMinutes);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/mcp`, baseUrl);\nconst capabilities = process.env.SANDBOX_CAPABILITIES;\nif (capabilities !== undefined) url.searchParams.set(\"capabilities\", capabilities);\nconst serverName = process.env.SANDBOX_SERVER_NAME;\nif (serverName !== undefined) url.searchParams.set(\"serverName\", serverName);\nconst ttlMinutes = process.env.SANDBOX_TTL_MINUTES;\nif (ttlMinutes !== undefined) url.searchParams.set(\"ttlMinutes\", ttlMinutes);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/mcp\"\nquery = {}\n_capabilities = os.environ.get(\"SANDBOX_CAPABILITIES\")\nif _capabilities is not None: query[\"capabilities\"] = _capabilities\n_serverName = os.environ.get(\"SANDBOX_SERVER_NAME\")\nif _serverName is not None: query[\"serverName\"] = _serverName\n_ttlMinutes = os.environ.get(\"SANDBOX_TTL_MINUTES\")\nif _ttlMinutes is not None: query[\"ttlMinutes\"] = _ttlMinutes\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/mcp\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_CAPABILITIES:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}capabilities=${SANDBOX_CAPABILITIES}\"\nfi\nif [ -n \"${SANDBOX_SERVER_NAME:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}serverName=${SANDBOX_SERVER_NAME}\"\nfi\nif [ -n \"${SANDBOX_TTL_MINUTES:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}ttlMinutes=${SANDBOX_TTL_MINUTES}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/native-credentials":{"post":{"tags":["Sandboxes"],"summary":"Install a native CLI credential in a sandbox","description":"Names a stored secret; no secret value is sent or returned. claude-code takes a token reference, codex a bundle or files reference. Needs write access to the sandbox, and a personal sandbox must be owned by the caller. Isolated sandboxes are refused. The grant is recorded as a sandbox.native_credential.grant audit event.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","codex"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false}},"required":["harness","cliAuth"],"additionalProperties":false}}}},"responses":{"200":{"description":"The reference is echoed back. A legacy seat replies installed: true. A canonical seat replies with its proxySecretReceipt and no installed field; status proxy-installed means the egress proxy admitted the seat and HOME delivery is still pending.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","codex"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"installed":{"type":"boolean","enum":[true]}},"required":["harness","cliAuth","installed"],"additionalProperties":false},{"type":"object","properties":{"harness":{"type":"string","enum":["claude-code","codex"]},"cliAuth":{"type":"object","properties":{"account":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9@._+-]{0,127}$"},"secretEnv":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,63}$"},"format":{"type":"string","enum":["token","bundle","files"]}},"required":["account","secretEnv","format"],"additionalProperties":false},"proxySecretReceipt":{"oneOf":[{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["proxy-installed"]},"materializationAcknowledged":{"type":"boolean","enum":[false]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["installed"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false},{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991},"bindings":{"type":"array","items":{"type":"object","properties":{"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"grant":{"type":"object","properties":{"protocolVersion":{"type":"number","enum":[1]},"grantId":{"type":"string","minLength":1,"maxLength":512},"sandboxId":{"type":"string","minLength":1,"maxLength":512},"epoch":{"type":"string","minLength":1,"maxLength":512},"identity":{"type":"object","properties":{"tenantId":{"type":"string","minLength":1,"maxLength":512},"issuerId":{"type":"string","minLength":1,"maxLength":512},"secretId":{"type":"string","minLength":1,"maxLength":512},"revision":{"type":"string","minLength":1,"maxLength":512}},"required":["tenantId","issuerId","secretId","revision"],"additionalProperties":false},"scope":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024}},"required":["hosts"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"sourceExpiresAt":{"type":"string","format":"date-time"},"parentGrantId":{"type":"string","minLength":1,"maxLength":512}},"required":["protocolVersion","grantId","sandboxId","epoch","identity","scope","presentation","placeholderFormat","issuedAt","expiresAt"],"additionalProperties":false}},"required":["envName","secretRef","grant"],"additionalProperties":false},"maxItems":1024},"proxyAcknowledged":{"type":"boolean","enum":[true]},"status":{"type":"string","enum":["revoked"]},"materializationAcknowledged":{"type":"boolean","enum":[true]}},"required":["protocolVersion","sandboxId","generation","bindings","proxyAcknowledged","status","materializationAcknowledged"],"additionalProperties":false}]}},"required":["harness","cliAuth","proxySecretReceipt"],"additionalProperties":false}]}}}},"400":{"description":"Invalid credential reference, or the sandbox's driver cannot be routed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential or team role cannot use this sandbox, the sandbox is isolated, or the caller does not own it (SANDBOX_KEY_RESOURCE_FORBIDDEN, INSUFFICIENT_ROLE, SANDBOX_ACCESS_DENIED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A held seat cannot switch live (NATIVE_CREDENTIAL_SEAT_SWITCH_UNSUPPORTED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Installation was not confirmed, or the sandbox service did not answer (NATIVE_CREDENTIAL_GRANT_UNAVAILABLE, HOST_UNAVAILABLE)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"message":{"type":"string"},"retryable":{"type":"boolean"}}}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/native-credentials`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/native-credentials`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/native-credentials\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/native-credentials\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/operations/{operationId}":{"get":{"summary":"Get an accepted stop, snapshot or delete","description":"Reports an operation a `wait=false` stop, snapshot or delete accepted. Poll until `state` is `succeeded` or `failed`. Still readable after the sandbox is deleted: a delete is authorized from the scope it was accepted under (its owner, or a current member of its team). Unknown ids and ids of other tenants answer the same 404. `waitMs` holds the read while it runs; a delete holds 10 s.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"Operation ID"},"required":true,"description":"Operation ID","name":"operationId","in":"path"},{"schema":{"type":"string","enum":["removed"]},"required":false,"name":"until","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":10000},"required":false,"name":"waitMs","in":"query"}],"responses":{"200":{"description":"Operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxOperation"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Operation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to read the operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid sandbox service response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The sandbox service is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst operationId = process.env.SANDBOX_OPERATION_ID;\nif (!operationId) throw new Error(\"Set SANDBOX_OPERATION_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/operations/${encodeURIComponent(operationId)}`, baseUrl);\nconst until = process.env.SANDBOX_UNTIL;\nif (until !== undefined) url.searchParams.set(\"until\", until);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst operationId = process.env.SANDBOX_OPERATION_ID;\nif (!operationId) throw new Error(\"Set SANDBOX_OPERATION_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/operations/${encodeURIComponent(operationId)}`, baseUrl);\nconst until = process.env.SANDBOX_UNTIL;\nif (until !== undefined) url.searchParams.set(\"until\", until);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_operationId = os.environ.get(\"SANDBOX_OPERATION_ID\")\nif not _operationId: raise SystemExit(\"Set SANDBOX_OPERATION_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/operations/{quote(_operationId or '', safe='')}\"\nquery = {}\n_until = os.environ.get(\"SANDBOX_UNTIL\")\nif _until is not None: query[\"until\"] = _until\n_waitMs = os.environ.get(\"SANDBOX_WAIT_MS\")\nif _waitMs is not None: query[\"waitMs\"] = _waitMs\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/operations/${SANDBOX_OPERATION_ID:?Set SANDBOX_OPERATION_ID}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_UNTIL:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}until=${SANDBOX_UNTIL}\"\nfi\nif [ -n \"${SANDBOX_WAIT_MS:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}waitMs=${SANDBOX_WAIT_MS}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/placement-attestation":{"get":{"tags":["Sandboxes"],"summary":"Read verified HIPAA placement evidence","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Server-issued placement evidence","content":{"application/json":{"schema":{"type":"object","properties":{"schemaVersion":{"type":"number","enum":[1]},"compliance":{"type":"string","enum":["hipaa"]},"hostId":{"type":"string","minLength":1},"provider":{"type":"string","minLength":1},"datacenter":{"type":"string","minLength":1},"scopeEntryId":{"type":"string","minLength":1},"baaReference":{"type":"string","minLength":1},"baaSignedAt":{"type":"string","format":"date-time"},"attestationReportReference":{"type":"string","minLength":1},"providerResourceId":{"type":"string","minLength":1},"provisionGeneration":{"type":"string","pattern":"^[a-f0-9]{64}$"},"attestedAt":{"type":"integer","exclusiveMinimum":0}},"required":["schemaVersion","compliance","hostId","provider","datacenter","scopeEntryId","baaReference","baaSignedAt","attestationReportReference","providerResourceId","provisionGeneration","attestedAt"],"additionalProperties":false}}}},"400":{"description":"Invalid sandbox target"},"403":{"description":"Sandbox access forbidden"},"404":{"description":"Sandbox or placement evidence not found"},"503":{"description":"Evidence unavailable"}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/placement-attestation`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/placement-attestation`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/placement-attestation\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/placement-attestation\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/rebuild":{"post":{"summary":"Rebuild a stopped sandbox runtime","description":"Requires a stopped sandbox. Confirms a durable snapshot, then creates a new runtime under the same sandbox ID. The optional env body resupplies public values that are not stored at rest. A refused rebuild keeps the stopped runtime.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"Sandbox resumed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeRequestRefusal"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Sandbox is not in a resumable lifecycle state, a new runtime needs env values only the caller holds (RESUME_ENV_REQUIRED), or its workspace could not be restored into a new runtime (RESUME_RESTORE_REFUSED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeRefusal"}}}},"429":{"description":"Concurrent session limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Integration bundle refresh failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Platform integrations unavailable","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"startupDiagnostics":{"type":"object","properties":{"operations":{"type":"array","items":{"type":"object","properties":{"operation":{"type":"string"},"service":{"type":"string"},"status":{"type":"string"},"durationMs":{"type":"number"},"parent":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["operation","service","status"]}},"requestId":{"type":"string"}},"required":["operations"]}}}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rebuild`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rebuild`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/rebuild\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/rebuild\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/resume":{"post":{"summary":"Resume a stopped sandbox","description":"Resumes a stopped sandbox. A sandbox that is already running answers 200 with its current state. The optional JSON body `{ \"env\": { \"KEY\": \"value\" } }` resupplies public env values that are not stored at rest, such as a per-run coordination token or a secret passed at create. A resume that must build a new runtime without them answers 409 `RESUME_ENV_REQUIRED` with `missingEnvKeys`. The values are used only when the resume builds a new runtime.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"Sandbox resumed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeRequestRefusal"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Sandbox is not in a resumable lifecycle state, a new runtime needs env values only the caller holds (RESUME_ENV_REQUIRED), or its workspace could not be restored into a new runtime (RESUME_RESTORE_REFUSED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeRefusal"}}}},"429":{"description":"Concurrent session limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Integration bundle refresh failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Platform integrations unavailable","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"startupDiagnostics":{"type":"object","properties":{"operations":{"type":"array","items":{"type":"object","properties":{"operation":{"type":"string"},"service":{"type":"string"},"status":{"type":"string"},"durationMs":{"type":"number"},"parent":{"type":"string"},"metadata":{"type":"object","additionalProperties":{}}},"required":["operation","service","status"]}},"requestId":{"type":"string"}},"required":["operations"]}}}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/resume`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/resume`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/resume\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/resume\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/rollouts":{"post":{"summary":"Fork a running agent sandbox N ways and run the same turn in each","description":"Speculative rollouts: branches the RUNNING sandbox into `n` copy-on-write children, sends the same message `parts` to a fresh agent session in every child in parallel, and (optionally) scores each child by running `scorer.command` inside it; exit 0 passes and stdout is parsed as the numeric score. Responds once the children exist; poll `GET /v1/sandboxes/{id}/rollouts/{rolloutId}` for turn results, per-child errors, and the winner. Children are KEPT for the caller to promote or delete: they are first-class sandboxes that live independently of the parent (deleting the parent reaps only the shared branch-base snapshots once the children are gone; a parent with live children refuses delete without `cascade=true`).","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"n":{"type":"integer","minimum":1,"maximum":8},"parts":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","minLength":1}},"required":["type"],"additionalProperties":{}},"minItems":1},"scorer":{"type":"object","properties":{"type":{"type":"string","enum":["none","command"]},"command":{"type":"string","minLength":1}},"required":["type"]}},"required":["n","parts"]}}}},"responses":{"202":{"description":"Children materialized; turns running","content":{"application/json":{"schema":{"type":"object","properties":{"rolloutId":{"type":"string"},"status":{"type":"string","enum":["running","completed","partial","failed"]},"children":{"type":"array","items":{"type":"string"}},"materializeTimeMs":{"type":"number"},"turnTimeoutMs":{"type":"number"}},"required":["rolloutId","status","children"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Concurrent session limit exceeded, or provisioning saturated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Active driver does not support live branching","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rollouts`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rollouts`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/rollouts\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/rollouts\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/rollouts/{rolloutId}":{"get":{"summary":"Poll a rollout's status and scored per-child results","description":"Failed or timed-out children appear as result entries with `error` set and no score; partial results are never dropped. `winner` is the child with the highest numeric score (command scorer only).","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1},"required":true,"name":"rolloutId","in":"path"}],"responses":{"200":{"description":"Rollout state","content":{"application/json":{"schema":{"type":"object","properties":{"rolloutId":{"type":"string"},"status":{"type":"string","enum":["running","completed","partial","failed"]},"children":{"type":"array","items":{"type":"string"}},"results":{"type":"array","items":{"type":"object","properties":{"sandboxId":{"type":"string"},"output":{"type":"string"},"error":{"type":"string"},"exitCode":{"type":"number"},"score":{"type":"number"},"durationMs":{"type":"number"}},"required":["sandboxId","durationMs"],"additionalProperties":{}}},"winner":{"type":"string"},"error":{"type":"string"},"materializeTimeMs":{"type":"number"},"turnTimeoutMs":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["rolloutId","status","children","results"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox or rollout not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst rolloutId = process.env.SANDBOX_ROLLOUT_ID;\nif (!rolloutId) throw new Error(\"Set SANDBOX_ROLLOUT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rollouts/${encodeURIComponent(rolloutId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst rolloutId = process.env.SANDBOX_ROLLOUT_ID;\nif (!rolloutId) throw new Error(\"Set SANDBOX_ROLLOUT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/rollouts/${encodeURIComponent(rolloutId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_rolloutId = os.environ.get(\"SANDBOX_ROLLOUT_ID\")\nif not _rolloutId: raise SystemExit(\"Set SANDBOX_ROLLOUT_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/rollouts/{quote(_rolloutId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/rollouts/${SANDBOX_ROLLOUT_ID:?Set SANDBOX_ROLLOUT_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/runtime/{path}":{"get":{"tags":["Sandboxes"],"summary":"Send a GET request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs read access to the sandbox. A GET with Upgrade: websocket to terminals/{connectionId}/ws or ssh opens a WebSocket and answers 101; a scoped token can open only the terminal socket. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"responses":{"101":{"description":"WebSocket upgrade accepted (terminal and SSH sub-paths)"},"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"401":{"description":"A WebSocket upgrade needs a signed-in user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"head":{"tags":["Sandboxes"],"summary":"Send a HEAD request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs read access to the sandbox. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"responses":{"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"HEAD\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"HEAD\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"HEAD\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\ncurl --fail-with-body --silent --show-error \\\n  --request HEAD \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Sandboxes"],"summary":"Send a POST request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs write access to the sandbox. POST agents/run answers when the run finishes, with { success: true, data } or { success: false, error } under the run's status. POST agents/run/stream answers with an event stream. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"put":{"tags":["Sandboxes"],"summary":"Send a PUT request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs write access to the sandbox. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"patch":{"tags":["Sandboxes"],"summary":"Send a PATCH request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs write access to the sandbox. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Sandboxes"],"summary":"Send a DELETE request to the sandbox runtime","description":"Forwards the request to the sandbox runtime at the same sub-path and relays its status, headers and body. Accepts an account API key or a scoped sandbox token from POST /v1/sandboxes/{id}/scoped-token. Needs write access to the sandbox. A request that starts paid compute, such as an agent run or exec, is refused with 402 or 429 while the paying account has debt or is below the balance floor, and it resumes a suspended sandbox first. Runtime error bodies are returned in the standard error shape.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash."},"required":true,"description":"Runtime sub-path, such as agents/run or files/tree. It cannot contain .. or start with a slash.","name":"path","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"responses":{"200":{"description":"The runtime's response, relayed with its own status, headers and body"},"400":{"description":"Invalid sandbox ID or sub-path, an execution time limit above the deployment maximum, or a request the runtime refused (INVALID_REQUEST, EXECUTION_TTL_EXCEEDS_LIMIT)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"402":{"description":"The paying account has recorded debt, or the model credential's budget is spent (INSUFFICIENT_BALANCE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"403":{"description":"The credential or team role cannot use this sandbox, a scoped token cannot use this sub-path, or secret isolation could not be proved","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Sandbox not found or already ended","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"429":{"description":"The plan's runtime request rate is reached, or the paying account is below the admission floor (RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"502":{"description":"An agent run's event stream ended without a result (AGENT_RUN_STREAM_INCOMPLETE)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]},{"$ref":"#/components/schemas/FailureEnvelope"}]}}}},"503":{"description":"The sandbox service or the runtime did not answer; retry after Retry-After","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst path = process.env.SANDBOX_PATH;\nif (!path) throw new Error(\"Set SANDBOX_PATH\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/runtime/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"DELETE\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_path = os.environ.get(\"SANDBOX_PATH\")\nif not _path: raise SystemExit(\"Set SANDBOX_PATH\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/runtime/{quote(_path or '', safe='/')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/runtime/${SANDBOX_PATH:?Set SANDBOX_PATH}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/scoped-token":{"post":{"summary":"Mint a scoped consumer-side token for direct sandbox access","description":"Session/project tokens read SessionGateway streams. Runtime tokens grant `session-runtime`: read + workspace + terminal, or `read-only`: sandbox-wide read. Set `access: read-only` on session-runtime for session-bound read alone; the response must echo that restriction. Runtime read includes screenshots; workspace adds file writes and terminal adds its WebSocket. No scope here grants desktop input, /a11y or /mcp: mint computer_use separately at `GET /v1/sandboxes/{id}/mcp?capabilities=computer_use`. Missing-capability 403 responses identify the required capability and its mint route.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"scope":{"type":"string","enum":["session"]},"sessionId":{"type":"string","minLength":1},"runtimeSessionId":{"type":"string","minLength":1},"ttlMinutes":{"type":"integer","minimum":1,"maximum":15}},"required":["scope","sessionId","runtimeSessionId"],"additionalProperties":false},{"type":"object","properties":{"scope":{"type":"string","enum":["session-runtime"]},"sessionId":{"type":"string","minLength":1},"access":{"type":"string","enum":["read-only"]},"ttlMinutes":{"type":"integer","minimum":1,"maximum":15}},"required":["scope","sessionId"],"additionalProperties":false},{"type":"object","properties":{"scope":{"type":"string","enum":["project","read-only"]},"ttlMinutes":{"type":"integer","minimum":1,"maximum":15}},"required":["scope"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Scoped token issued","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"expiresAt":{"type":"number"},"runtimeUrl":{"type":"string"},"sidecarProxyUrl":{"type":"string"},"scope":{"type":"string","enum":["session","session-runtime","project","read-only"]},"access":{"type":"string","enum":["read-only"]}},"required":["token","expiresAt","runtimeUrl","sidecarProxyUrl","scope"]}}}},"400":{"description":"Invalid scope or missing sessionId for session scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Caller does not own this sandbox","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid sandbox service response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/scoped-token`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/scoped-token`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/scoped-token\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/scoped-token\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{id}/snapshot":{"post":{"summary":"Create a snapshot of sandbox state","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"wait","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."},"required":false,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation.","name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Snapshot created, or the stored result of an earlier create","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotResponse"}}}},"202":{"description":"Snapshot accepted; it is captured in the background","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SandboxOperationAccepted"},{"type":"object","properties":{"idempotency":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","replayed"]},"requestDigest":{"type":"string"}},"required":["outcome","requestDigest"]}}}]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Snapshot limit reached, or this idempotency key is bound to a different request body","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/WorkspaceOperationConflict"}]}}}},"500":{"description":"Snapshot failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream snapshot service returned success without a snapshot ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Snapshot quota check unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshot`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshot`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshot\"\nquery = {}\n_wait = os.environ.get(\"SANDBOX_WAIT\")\nif _wait is not None: query[\"wait\"] = _wait\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshot\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_WAIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}wait=${SANDBOX_WAIT}\"\nfi\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]}},"/v1/sandboxes/{id}/snapshots":{"post":{"summary":"Create a snapshot of sandbox state","description":"Answers 202 once capture is durably accepted. Poll `operation.statusUrl` until `succeeded` or `failed`; a successful result names the snapshot. Writes made before the request are included. Wait for completion before writing data that must remain outside the snapshot.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"wait","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."},"required":false,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation.","name":"Idempotency-Key","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Snapshot created, or the stored result of an earlier create","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotResponse"}}}},"202":{"description":"Snapshot accepted; it is captured in the background","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/SandboxOperationAccepted"},{"type":"object","properties":{"idempotency":{"type":"object","properties":{"outcome":{"type":"string","enum":["created","replayed"]},"requestDigest":{"type":"string"}},"required":["outcome","requestDigest"]}}}]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Snapshot limit reached, or this idempotency key is bound to a different request body","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/WorkspaceOperationConflict"}]}}}},"500":{"description":"Snapshot failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream snapshot service returned success without a snapshot ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Snapshot quota check unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshots\"\nquery = {}\n_wait = os.environ.get(\"SANDBOX_WAIT\")\nif _wait is not None: query[\"wait\"] = _wait\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshots\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_WAIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}wait=${SANDBOX_WAIT}\"\nfi\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]},"get":{"summary":"List snapshots for a sandbox","description":"For a deleted sandbox whose final snapshot is retained, its owner gets that snapshot alone, marked `final`, with `deleted: true`. Anyone else, a team member included, gets 404, as for a sandbox with no retained snapshot.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"responses":{"200":{"description":"List of snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotListResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Snapshot listing failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Snapshot backend unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Retryable snapshot service failure","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"retryable":{"type":"boolean"}},"required":["retryable"]}]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshots\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshots\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/snapshots/jobs/{jobId}":{"get":{"summary":"Get sandbox snapshot restore job status","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Restore job ID"},"required":true,"description":"Restore job ID","name":"jobId","in":"path"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":15000,"description":"Hold the answer until the job finishes, up to this many milliseconds"},"required":false,"description":"Hold the answer until the job finishes, up to this many milliseconds","name":"waitMs","in":"query"}],"responses":{"200":{"description":"Restore job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotRestoreJobStatusResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to fetch restore job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Restore job queue unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/jobs/${encodeURIComponent(jobId)}`, baseUrl);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst jobId = process.env.SANDBOX_JOB_ID;\nif (!jobId) throw new Error(\"Set SANDBOX_JOB_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/jobs/${encodeURIComponent(jobId)}`, baseUrl);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_jobId = os.environ.get(\"SANDBOX_JOB_ID\")\nif not _jobId: raise SystemExit(\"Set SANDBOX_JOB_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshots/jobs/{quote(_jobId or '', safe='')}\"\nquery = {}\n_waitMs = os.environ.get(\"SANDBOX_WAIT_MS\")\nif _waitMs is not None: query[\"waitMs\"] = _waitMs\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshots/jobs/${SANDBOX_JOB_ID:?Set SANDBOX_JOB_ID}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_WAIT_MS:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}waitMs=${SANDBOX_WAIT_MS}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/snapshots/{snapshotId}":{"delete":{"summary":"Delete a sandbox snapshot","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Snapshot ID"},"required":true,"description":"Snapshot ID","name":"snapshotId","in":"path"}],"responses":{"200":{"description":"Snapshot deleted, or already absent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"snapshotId":{"type":"string"},"outcome":{"type":"string","enum":["deleted","already_absent","unknown"]}},"required":["success","snapshotId","outcome"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Delete failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Snapshot backend unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Host restarting or unreachable; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/${encodeURIComponent(snapshotId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/${encodeURIComponent(snapshotId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_snapshotId = os.environ.get(\"SANDBOX_SNAPSHOT_ID\")\nif not _snapshotId: raise SystemExit(\"Set SANDBOX_SNAPSHOT_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshots/{quote(_snapshotId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshots/${SANDBOX_SNAPSHOT_ID:?Set SANDBOX_SNAPSHOT_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/snapshots/{snapshotId}/restore":{"post":{"summary":"Restore a sandbox from a snapshot","description":"Restores the whole workspace from the snapshot. The optional JSON body `{ \"paths\": [\"relative/path\"] }` restores only those workspace-relative paths and leaves every other file untouched; an empty body restores the whole workspace. A body that is present but is not that JSON object answers 400, and nothing is restored. With `waitMs` the 202 is held until the job finishes and carries its status, so the caller needs no poll. A repeat under the same `Idempotency-Key` answers the same job for an hour after it ends; the same key with another snapshot or paths is another restore.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Snapshot ID"},"required":true,"description":"Snapshot ID","name":"snapshotId","in":"path"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":15000,"description":"Hold the answer until the job finishes, up to this many milliseconds"},"required":false,"description":"Hold the answer until the job finishes, up to this many milliseconds","name":"waitMs","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."},"required":false,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation.","name":"Idempotency-Key","in":"header"}],"responses":{"200":{"description":"Snapshot restored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotResponse"}}}},"202":{"description":"Restore accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SnapshotRestoreAcceptedResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox or snapshot not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Restore failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Snapshot backend unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Invalid sandbox service response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/${encodeURIComponent(snapshotId)}/restore`, baseUrl);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst snapshotId = process.env.SANDBOX_SNAPSHOT_ID;\nif (!snapshotId) throw new Error(\"Set SANDBOX_SNAPSHOT_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/snapshots/${encodeURIComponent(snapshotId)}/restore`, baseUrl);\nconst waitMs = process.env.SANDBOX_WAIT_MS;\nif (waitMs !== undefined) url.searchParams.set(\"waitMs\", waitMs);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst Idempotency_Key = process.env.SANDBOX_IDEMPOTENCY_KEY;\nif (Idempotency_Key !== undefined) headers[\"Idempotency-Key\"] = Idempotency_Key;\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_snapshotId = os.environ.get(\"SANDBOX_SNAPSHOT_ID\")\nif not _snapshotId: raise SystemExit(\"Set SANDBOX_SNAPSHOT_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/snapshots/{quote(_snapshotId or '', safe='')}/restore\"\nquery = {}\n_waitMs = os.environ.get(\"SANDBOX_WAIT_MS\")\nif _waitMs is not None: query[\"waitMs\"] = _waitMs\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n_Idempotency_Key = os.environ.get(\"SANDBOX_IDEMPOTENCY_KEY\")\nif _Idempotency_Key is not None: headers[\"Idempotency-Key\"] = _Idempotency_Key\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/snapshots/${SANDBOX_SNAPSHOT_ID:?Set SANDBOX_SNAPSHOT_ID}/restore\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_WAIT_MS:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}waitMs=${SANDBOX_WAIT_MS}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Idempotency-Key: ${SANDBOX_IDEMPOTENCY_KEY:?Set SANDBOX_IDEMPOTENCY_KEY}\""}]}},"/v1/sandboxes/{id}/stop":{"post":{"summary":"Stop a sandbox (keeps state)","description":"Answers once the sandbox is stopped and its final workspace generation is durable. With `wait=false` it may instead answer 202 as soon as the stop is durably accepted: poll `operation.statusUrl` (or `Location`) until the operation is `succeeded` or `failed`. Billing runs until the runtime is actually stopped, and a resume issued meanwhile waits for the stop. A 200 to a `wait=false` request means the stop already finished synchronously. A synchronous stop accepts `{ \"retain\": { \"paths\": [\"relative/path\"] } }` to export its exact remotely confirmed stopped workspace before answering.","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"wait","in":"query"}],"responses":{"200":{"description":"Sandbox stopped","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxResponse"}}}},"202":{"description":"Stop accepted; it finishes in the background","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxOperationAccepted"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Refused with a typed end: SUSPEND_REQUIRES_MANAGED_HOME, RUNTIME_GONE, FINAL_SUSPEND_UNCONFIRMED or RUN_SESSION_CAPTURE_PENDING; `retryable: false` answers every repeat the same","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"retryable":{"type":"boolean"}}}]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Sandbox billing state is temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/stop`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/stop`, baseUrl);\nconst wait = process.env.SANDBOX_WAIT;\nif (wait !== undefined) url.searchParams.set(\"wait\", wait);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/stop\"\nquery = {}\n_wait = os.environ.get(\"SANDBOX_WAIT\")\nif _wait is not None: query[\"wait\"] = _wait\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/stop\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_WAIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}wait=${SANDBOX_WAIT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/trace":{"get":{"summary":"Export sandbox trace and intelligence envelope","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"includeIntelligence","in":"query"}],"responses":{"200":{"description":"Sandbox trace export","content":{"application/json":{"schema":{"type":"object","properties":{"trace":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["sandbox.trace.v1"]},"traceId":{"type":"string"},"sandboxId":{"type":"string"},"exportedAt":{"type":"string"},"timings":{"type":"object","properties":{"observedLifecycleMs":{"type":"number","minimum":0},"observedRuntimeMs":{"type":"number","minimum":0},"idleObservedMs":{"type":"number","minimum":0}},"required":["observedLifecycleMs","observedRuntimeMs","idleObservedMs"]},"criticalPath":{"type":"object","properties":{"durationMs":{"type":"number","minimum":0},"phases":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"durationMs":{"type":"number","minimum":0}},"required":["name","durationMs"]}}},"required":["durationMs","phases"]},"events":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox.lifecycle.snapshot","sandbox.runtime.snapshot","sandbox.usage.snapshot","sandbox.insight.summary"]},"timestamp":{"type":"string"},"sandboxId":{"type":"string"},"durationMs":{"type":"number","minimum":0},"attributes":{"type":"object","additionalProperties":{}}},"required":["type","timestamp","sandboxId","attributes"]}}},"required":["schemaVersion","traceId","sandboxId","exportedAt","timings","criticalPath","events"]},"intelligence":{"type":"object","properties":{"schemaVersion":{"type":"string","enum":["sandbox.intelligence.v1"]},"source":{"type":"string","enum":["sandbox-api"]},"subject":{"type":"object","properties":{"type":{"type":"string","enum":["sandbox"]},"sandboxId":{"type":"string"}},"required":["type","sandboxId"]},"billing":{"type":"object","properties":{"billable":{"type":"boolean","enum":[false]},"billedTo":{"type":"string","enum":["platform"]},"costUsd":{"type":"number","enum":[0]},"reason":{"type":"string","enum":["deterministic_platform_insight"]}},"required":["billable","billedTo","costUsd","reason"]},"metrics":{"type":"object","additionalProperties":{"type":"number"}},"signals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"severity":{"type":"string","enum":["info","warn","critical"]},"rationale":{"type":"string"}},"required":["name","value","severity","rationale"]}},"recommendedActions":{"type":"array","items":{"type":"string"}}},"required":["schemaVersion","source","subject","billing","metrics","signals","recommendedActions"]}},"required":["trace"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/trace`, baseUrl);\nconst includeIntelligence = process.env.SANDBOX_INCLUDE_INTELLIGENCE;\nif (includeIntelligence !== undefined) url.searchParams.set(\"includeIntelligence\", includeIntelligence);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/trace`, baseUrl);\nconst includeIntelligence = process.env.SANDBOX_INCLUDE_INTELLIGENCE;\nif (includeIntelligence !== undefined) url.searchParams.set(\"includeIntelligence\", includeIntelligence);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/trace\"\nquery = {}\n_includeIntelligence = os.environ.get(\"SANDBOX_INCLUDE_INTELLIGENCE\")\nif _includeIntelligence is not None: query[\"includeIntelligence\"] = _includeIntelligence\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/trace\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_INCLUDE_INTELLIGENCE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}includeIntelligence=${SANDBOX_INCLUDE_INTELLIGENCE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{id}/workspace-operations/lookup":{"post":{"summary":"Look up an idempotent workspace operation","tags":["Sandboxes"],"parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["checkpoint","fork"]},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"description":"Opt-in retry identity. A retry with this key and the same body replays the stored result instead of running a second operation."},"request":{"type":"object","additionalProperties":{}}},"required":["kind","idempotencyKey"],"additionalProperties":false}}}},"responses":{"200":{"description":"Operation lookup outcome","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceOperationLookup"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/workspace-operations/lookup`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(id)}/workspace-operations/lookup`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_id or '', safe='')}/workspace-operations/lookup\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_ID:?Set SANDBOX_ID}/workspace-operations/lookup\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sandboxes/{projectRef}/agent-sessions/{agentSessionId}/plans/current":{"get":{"tags":["Durable plans"],"summary":"Get the current plan of an agent session","description":"Needs read access to the sandbox. A plan not confirmed is not found.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"projectRef","in":"path"},{"schema":{"type":"string","description":"Agent session ID"},"required":true,"description":"Agent session ID","name":"agentSessionId","in":"path"}],"responses":{"200":{"description":"Current plan","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/DurablePlan"}},"required":["plan"]}}}},"403":{"description":"The credential or team role cannot use this sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/current`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/current`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_projectRef = os.environ.get(\"SANDBOX_PROJECT_REF\")\nif not _projectRef: raise SystemExit(\"Set SANDBOX_PROJECT_REF\")\n_agentSessionId = os.environ.get(\"SANDBOX_AGENT_SESSION_ID\")\nif not _agentSessionId: raise SystemExit(\"Set SANDBOX_AGENT_SESSION_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_projectRef or '', safe='')}/agent-sessions/{quote(_agentSessionId or '', safe='')}/plans/current\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_PROJECT_REF:?Set SANDBOX_PROJECT_REF}/agent-sessions/${SANDBOX_AGENT_SESSION_ID:?Set SANDBOX_AGENT_SESSION_ID}/plans/current\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{projectRef}/agent-sessions/{agentSessionId}/plans/{planId}":{"get":{"tags":["Durable plans"],"summary":"Get a plan","description":"Needs read access to the sandbox. A plan not confirmed is not found.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"projectRef","in":"path"},{"schema":{"type":"string","description":"Agent session ID"},"required":true,"description":"Agent session ID","name":"agentSessionId","in":"path"},{"schema":{"type":"string","description":"Plan ID"},"required":true,"description":"Plan ID","name":"planId","in":"path"}],"responses":{"200":{"description":"Plan","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/DurablePlan"}},"required":["plan"]}}}},"403":{"description":"The credential or team role cannot use this sandbox","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/${encodeURIComponent(planId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/${encodeURIComponent(planId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_projectRef = os.environ.get(\"SANDBOX_PROJECT_REF\")\nif not _projectRef: raise SystemExit(\"Set SANDBOX_PROJECT_REF\")\n_agentSessionId = os.environ.get(\"SANDBOX_AGENT_SESSION_ID\")\nif not _agentSessionId: raise SystemExit(\"Set SANDBOX_AGENT_SESSION_ID\")\n_planId = os.environ.get(\"SANDBOX_PLAN_ID\")\nif not _planId: raise SystemExit(\"Set SANDBOX_PLAN_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_projectRef or '', safe='')}/agent-sessions/{quote(_agentSessionId or '', safe='')}/plans/{quote(_planId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_PROJECT_REF:?Set SANDBOX_PROJECT_REF}/agent-sessions/${SANDBOX_AGENT_SESSION_ID:?Set SANDBOX_AGENT_SESSION_ID}/plans/${SANDBOX_PLAN_ID:?Set SANDBOX_PLAN_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sandboxes/{projectRef}/agent-sessions/{agentSessionId}/plans/{planId}/decision":{"post":{"tags":["Durable plans"],"summary":"Approve or reject a plan","description":"Needs write access to the sandbox; a team viewer is refused. Repeating the same decision answers idempotent: true. Send followUp { mode: \"caller\" } to receive the follow-up turn's resume credential instead of having the service start the turn.","parameters":[{"schema":{"type":"string","description":"Sandbox ID"},"required":true,"description":"Sandbox ID","name":"projectRef","in":"path"},{"schema":{"type":"string","description":"Agent session ID"},"required":true,"description":"Agent session ID","name":"agentSessionId","in":"path"},{"schema":{"type":"string","description":"Plan ID"},"required":true,"description":"Plan ID","name":"planId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"outcome":{"type":"string","enum":["approved"]},"followUp":{"type":"object","properties":{"mode":{"type":"string","enum":["caller"]}},"required":["mode"],"additionalProperties":false}},"required":["outcome"],"additionalProperties":false},{"type":"object","properties":{"outcome":{"type":"string","enum":["rejected"]},"feedback":{"type":"string","minLength":1,"maxLength":32768},"followUp":{"type":"object","properties":{"mode":{"type":"string","enum":["caller"]}},"required":["mode"],"additionalProperties":false}},"required":["outcome","feedback"],"additionalProperties":false}]}}}},"responses":{"200":{"description":"Decision recorded","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/DurablePlan"},"followUp":{"type":"object","properties":{"turnId":{"type":"string"},"state":{"type":"string","enum":["awaiting_caller","queued","leased","started","completed","failed"]},"mode":{"type":"string","enum":["caller"]},"executionId":{"type":"string"},"resume":{"type":"object","properties":{"sourceTurnId":{"type":"string"},"continuation":{"type":"object","properties":{"version":{"type":"number","enum":[1]},"plan":{"type":"object","properties":{"id":{"type":"string","minLength":1},"revision":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string","minLength":1},"body":{"type":"string","minLength":1},"submittedAt":{"type":"string","format":"date-time"}},"required":["id","revision","body","submittedAt"],"additionalProperties":false},"sourceToolCallId":{"type":"string","minLength":1,"maxLength":256},"decision":{"oneOf":[{"type":"object","properties":{"outcome":{"type":"string","enum":["approved"]}},"required":["outcome"],"additionalProperties":false},{"type":"object","properties":{"outcome":{"type":"string","enum":["rejected"]},"feedback":{"type":"string","minLength":1,"maxLength":32768}},"required":["outcome","feedback"],"additionalProperties":false}]},"providerState":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["claude-code"]},"version":{"type":"number","enum":[1]},"nativeSessionId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","nativeSessionId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["codex"]},"version":{"type":"number","enum":[1]},"threadId":{"type":"string","minLength":1,"maxLength":256}},"required":["kind","version","threadId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","enum":["opencode"]},"version":{"type":"number","enum":[1]}},"required":["kind","version"],"additionalProperties":false}]}},"required":["version","plan","sourceToolCallId","decision","providerState"],"additionalProperties":false},"credential":{"type":"string"},"callbackUrl":{"type":"string"},"expiresAt":{"type":"string"}},"required":["sourceTurnId","continuation","credential","callbackUrl","expiresAt"]}},"required":["turnId","state"]},"idempotent":{"type":"boolean"}},"required":["plan","followUp","idempotent"]}}}},"400":{"description":"Invalid decision (INVALID_REQUEST, PLAN_REJECTION_FEEDBACK_REQUIRED, PLAN_APPROVAL_FEEDBACK_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential or team role cannot decide this plan (INSUFFICIENT_ROLE, PLAN_DECISION_ACTOR_INVALID)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"404":{"description":"Plan not found (PLAN_NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The plan cannot be decided in its current state, or its caller follow-up is leased (PLAN_SUPERSEDED, PLAN_WITHDRAWN, PLAN_ALREADY_DECIDED, PLAN_OUTBOX_LEASE_LOST)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"The request body is over the proxy limit","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}]}}}},"503":{"description":"Caller follow-up admission is not configured (PLAN_FOLLOWUP_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/${encodeURIComponent(planId)}/decision`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst projectRef = process.env.SANDBOX_PROJECT_REF;\nif (!projectRef) throw new Error(\"Set SANDBOX_PROJECT_REF\");\nconst agentSessionId = process.env.SANDBOX_AGENT_SESSION_ID;\nif (!agentSessionId) throw new Error(\"Set SANDBOX_AGENT_SESSION_ID\");\nconst planId = process.env.SANDBOX_PLAN_ID;\nif (!planId) throw new Error(\"Set SANDBOX_PLAN_ID\");\nconst url = new URL(`/v1/sandboxes/${encodeURIComponent(projectRef)}/agent-sessions/${encodeURIComponent(agentSessionId)}/plans/${encodeURIComponent(planId)}/decision`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_projectRef = os.environ.get(\"SANDBOX_PROJECT_REF\")\nif not _projectRef: raise SystemExit(\"Set SANDBOX_PROJECT_REF\")\n_agentSessionId = os.environ.get(\"SANDBOX_AGENT_SESSION_ID\")\nif not _agentSessionId: raise SystemExit(\"Set SANDBOX_AGENT_SESSION_ID\")\n_planId = os.environ.get(\"SANDBOX_PLAN_ID\")\nif not _planId: raise SystemExit(\"Set SANDBOX_PLAN_ID\")\nurl = f\"{base_url}/v1/sandboxes/{quote(_projectRef or '', safe='')}/agent-sessions/{quote(_agentSessionId or '', safe='')}/plans/{quote(_planId or '', safe='')}/decision\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sandboxes/${SANDBOX_PROJECT_REF:?Set SANDBOX_PROJECT_REF}/agent-sessions/${SANDBOX_AGENT_SESSION_ID:?Set SANDBOX_AGENT_SESSION_ID}/plans/${SANDBOX_PLAN_ID:?Set SANDBOX_PLAN_ID}/decision\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/secrets":{"get":{"tags":["Secrets"],"summary":"List secrets (names only, no values)","responses":{"200":{"description":"List of secret names","content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["name","createdAt"],"additionalProperties":false}}},"required":["secrets"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/secrets`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/secrets`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/secrets\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/secrets\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Secrets"],"summary":"Create an encrypted secret","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"value":{"type":"string","minLength":1,"maxLength":65536},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"}},"required":["name","value"],"additionalProperties":false}}}},"responses":{"201":{"description":"Secret created","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["name","createdAt"],"additionalProperties":false}},"required":["secret"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Secret already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/secrets`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/secrets`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/secrets\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/secrets\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/secrets/{name}":{"put":{"tags":["Secrets"],"summary":"Replace a secret and re-key live sandboxes","parameters":[{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"required":true,"name":"name","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":65536},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"},"expectedUpdatedAt":{"type":"string","format":"date-time"}},"required":["value"],"additionalProperties":false}}}},"responses":{"200":{"description":"Secret created","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"type":"object","properties":{"name":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["name","createdAt"],"additionalProperties":false}},"required":["secret"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Secret already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"503":{"description":"Live re-key not acknowledged","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_name = os.environ.get(\"SANDBOX_NAME\")\nif not _name: raise SystemExit(\"Set SANDBOX_NAME\")\nurl = f\"{base_url}/v1/secrets/{quote(_name or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/secrets/${SANDBOX_NAME:?Set SANDBOX_NAME}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Secrets"],"summary":"Delete a secret","parameters":[{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"expectedUpdatedAt","in":"query"}],"responses":{"200":{"description":"Secret deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Generation changed","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst expectedUpdatedAt = process.env.SANDBOX_EXPECTED_UPDATED_AT;\nif (expectedUpdatedAt !== undefined) url.searchParams.set(\"expectedUpdatedAt\", expectedUpdatedAt);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst expectedUpdatedAt = process.env.SANDBOX_EXPECTED_UPDATED_AT;\nif (expectedUpdatedAt !== undefined) url.searchParams.set(\"expectedUpdatedAt\", expectedUpdatedAt);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_name = os.environ.get(\"SANDBOX_NAME\")\nif not _name: raise SystemExit(\"Set SANDBOX_NAME\")\nurl = f\"{base_url}/v1/secrets/{quote(_name or '', safe='')}\"\nquery = {}\n_expectedUpdatedAt = os.environ.get(\"SANDBOX_EXPECTED_UPDATED_AT\")\nif _expectedUpdatedAt is not None: query[\"expectedUpdatedAt\"] = _expectedUpdatedAt\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/secrets/${SANDBOX_NAME:?Set SANDBOX_NAME}\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_EXPECTED_UPDATED_AT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}expectedUpdatedAt=${SANDBOX_EXPECTED_UPDATED_AT}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/session/{sessionId}/broadcast":{"post":{"tags":["Sessions"],"summary":"Broadcast an event to a session","description":"Sends an event to the caller's connected WebSocket session. If the session is not connected, the event is buffered for delivery on connect. success reports whether the event was delivered or buffered.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"sessionId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","minLength":1,"description":"Event type."},"id":{"type":"string","description":"Event ID the receiver can use to deduplicate."},"data":{"description":"Event payload."},"channel":{"type":"string","description":"Deliver only to subscribers of this channel."}},"required":["type"]}}}},"responses":{"200":{"description":"The broadcast result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId"]}}}},"400":{"description":"The session ID or the body is invalid (VALIDATION_ERROR, INVALID_SESSION_ID)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"],"additionalProperties":{}}},"required":["error"],"additionalProperties":{}}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The sandbox service did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/broadcast`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/broadcast`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sessionId = os.environ.get(\"SANDBOX_SESSION_ID\")\nif not _sessionId: raise SystemExit(\"Set SANDBOX_SESSION_ID\")\nurl = f\"{base_url}/v1/session/{quote(_sessionId or '', safe='')}/broadcast\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/session/${SANDBOX_SESSION_ID:?Set SANDBOX_SESSION_ID}/broadcast\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/session/{sessionId}/mapping":{"put":{"tags":["Sessions"],"summary":"Map a session to its runtime session","description":"Registers which runtime session serves the session ID, so session events are filtered and delivered to the right subscribers.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"sessionId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sidecarSessionId":{"type":"string","minLength":1,"description":"The runtime's own session ID (a UUID)."},"sidecarId":{"type":"string","description":"Container ID, for direct routing."},"projectRef":{"type":"string","description":"Project reference, for stale-runtime checks."}},"required":["sidecarSessionId"]}}}},"responses":{"200":{"description":"The mapping was recorded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"sidecarSessionId":{"type":"string"},"reprovisionRequired":{"type":"boolean","enum":[true]},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId","sidecarSessionId"]}}}},"400":{"description":"The session ID or the body is invalid (VALIDATION_ERROR, INVALID_SESSION_ID)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"],"additionalProperties":{}}},"required":["error"],"additionalProperties":{}}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The mapping was rejected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"sidecarSessionId":{"type":"string"},"reprovisionRequired":{"type":"boolean","enum":[true]},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId","sidecarSessionId"]}}}},"410":{"description":"The runtime is gone and must be reprovisioned","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"sidecarSessionId":{"type":"string"},"reprovisionRequired":{"type":"boolean","enum":[true]},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId","sidecarSessionId"]}}}},"503":{"description":"The sandbox service did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/mapping`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/mapping`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sessionId = os.environ.get(\"SANDBOX_SESSION_ID\")\nif not _sessionId: raise SystemExit(\"Set SANDBOX_SESSION_ID\")\nurl = f\"{base_url}/v1/session/{quote(_sessionId or '', safe='')}/mapping\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/session/${SANDBOX_SESSION_ID:?Set SANDBOX_SESSION_ID}/mapping\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Sessions"],"summary":"Remove a session's runtime mapping","description":"Removes the mapping and stops event subscriptions for the session.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"sessionId","in":"path"}],"responses":{"200":{"description":"The mapping was removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId"]}}}},"400":{"description":"The session ID or the body is invalid (VALIDATION_ERROR, INVALID_SESSION_ID)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"],"additionalProperties":{}}},"required":["error"],"additionalProperties":{}}}}},"401":{"description":"Missing or invalid credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialRefusal"}}}},"403":{"description":"The credential is restricted to its own sandbox (SANDBOX_KEY_RESOURCE_FORBIDDEN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The session has no mapping","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"sessionId":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"retryable":{"type":"boolean"}},"required":["success","sessionId"]}}}},"503":{"description":"The sandbox service did not answer; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/mapping`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst sessionId = process.env.SANDBOX_SESSION_ID;\nif (!sessionId) throw new Error(\"Set SANDBOX_SESSION_ID\");\nconst url = new URL(`/v1/session/${encodeURIComponent(sessionId)}/mapping`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_sessionId = os.environ.get(\"SANDBOX_SESSION_ID\")\nif not _sessionId: raise SystemExit(\"Set SANDBOX_SESSION_ID\")\nurl = f\"{base_url}/v1/session/{quote(_sessionId or '', safe='')}/mapping\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/session/${SANDBOX_SESSION_ID:?Set SANDBOX_SESSION_ID}/mapping\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sessions":{"post":{"summary":"Create a session","tags":["Sessions"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"KEY_BUDGET_EXHAUSTED: the Platform key funding the sandbox has no budget left","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed, or PROFILE_SECRET_ISOLATION_UNAVAILABLE: backend.metadata.profileSecretIsolation needs an existing sandbox whose runtime seals the secret launch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sandbox not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Request body exceeds the proxy wire limit","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string","enum":["PAYLOAD_TOO_LARGE"]},"message":{"type":"string"},"surface":{"type":"string"},"limitBytes":{"type":"integer","exclusiveMinimum":0},"actualBytes":{"type":"integer","exclusiveMinimum":0}},"required":["code","message","surface","limitBytes"]}},"required":["success","error"]},{"$ref":"#/components/schemas/ErrorResponse"}]}}}},"429":{"description":"Quota exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Runtime host unavailable, or PROFILE_SECRET_ISOLATION_UNAVAILABLE with reason sidecar_unreachable: the sandbox's runtime could not be asked; retry after Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sessions`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/sessions`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/sessions\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sessions/{id}":{"get":{"summary":"Get session details","tags":["Sessions"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"id","in":"path"}],"responses":{"200":{"description":"Session details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"delete":{"summary":"End a session","tags":["Sessions"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"id","in":"path"}],"responses":{"200":{"description":"Session ended","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Sandbox billing state is temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sessions/{id}/events":{"get":{"summary":"Subscribe to session events (SSE)","tags":["Sessions"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"id","in":"path"}],"responses":{"200":{"description":"SSE stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/events`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/events`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}/events\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}/events\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sessions/{id}/message":{"post":{"summary":"Send a message to a session and stream events","tags":["Sessions"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"200":{"description":"Server-Sent Events stream","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The sandbox was created without an agent","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"413":{"description":"Message body exceeds the proxy wire limit","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string","enum":["PAYLOAD_TOO_LARGE"]},"message":{"type":"string"},"surface":{"type":"string"},"limitBytes":{"type":"integer","exclusiveMinimum":0},"actualBytes":{"type":"integer","exclusiveMinimum":0}},"required":["code","message","surface","limitBytes"]}},"required":["success","error"]},{"$ref":"#/components/schemas/ErrorResponse"}]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Model credential is being updated; retry shortly","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/message`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/message`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}/message\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}/message\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/sessions/{id}/messages":{"get":{"summary":"List messages for a session","description":"Returns the session message log including in-flight assistant content. Each run's assistant message carries `metadata.status` (`streaming` / `completed` / `interrupted`).","tags":["Sessions"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Session ID"},"required":true,"description":"Session ID","name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":1000},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":["integer","null"]},"required":false,"name":"since","in":"query"}],"responses":{"200":{"description":"Message list","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":{}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed or insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/messages`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst since = process.env.SANDBOX_SINCE;\nif (since !== undefined) url.searchParams.set(\"since\", since);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/messages`, baseUrl);\nconst limit = process.env.SANDBOX_LIMIT;\nif (limit !== undefined) url.searchParams.set(\"limit\", limit);\nconst offset = process.env.SANDBOX_OFFSET;\nif (offset !== undefined) url.searchParams.set(\"offset\", offset);\nconst since = process.env.SANDBOX_SINCE;\nif (since !== undefined) url.searchParams.set(\"since\", since);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}/messages\"\nquery = {}\n_limit = os.environ.get(\"SANDBOX_LIMIT\")\nif _limit is not None: query[\"limit\"] = _limit\n_offset = os.environ.get(\"SANDBOX_OFFSET\")\nif _offset is not None: query[\"offset\"] = _offset\n_since = os.environ.get(\"SANDBOX_SINCE\")\nif _since is not None: query[\"since\"] = _since\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}/messages\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_LIMIT:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}limit=${SANDBOX_LIMIT}\"\nfi\nif [ -n \"${SANDBOX_OFFSET:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}offset=${SANDBOX_OFFSET}\"\nfi\nif [ -n \"${SANDBOX_SINCE:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}since=${SANDBOX_SINCE}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/sessions/{id}/turns/{turnId}":{"get":{"summary":"Look up a cached turn result by idempotency key","description":"Returns the cached AgentExecutionResult for a previously-completed turn on this session, or 404 if no such completion exists. Callers use this to short-circuit retries whose original attempt already finished.","tags":["Sessions"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":128},"required":true,"name":"turnId","in":"path"}],"responses":{"200":{"description":"Cached turn result","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Driver not allowed or insufficient access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session or turn not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream proxy error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst turnId = process.env.SANDBOX_TURN_ID;\nif (!turnId) throw new Error(\"Set SANDBOX_TURN_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/turns/${encodeURIComponent(turnId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst turnId = process.env.SANDBOX_TURN_ID;\nif (!turnId) throw new Error(\"Set SANDBOX_TURN_ID\");\nconst url = new URL(`/v1/sessions/${encodeURIComponent(id)}/turns/${encodeURIComponent(turnId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_turnId = os.environ.get(\"SANDBOX_TURN_ID\")\nif not _turnId: raise SystemExit(\"Set SANDBOX_TURN_ID\")\nurl = f\"{base_url}/v1/sessions/{quote(_id or '', safe='')}/turns/{quote(_turnId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/sessions/${SANDBOX_ID:?Set SANDBOX_ID}/turns/${SANDBOX_TURN_ID:?Set SANDBOX_TURN_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/shapes":{"get":{"tags":["Sandboxes"],"summary":"List sandbox shape presets and the caller's ceiling","description":"Returns the named sandbox sizes, which one is the platform's default, and the largest shape this account can be granted on this deployment. Presets are the platform's numbers and are not clamped per account: a preset above `ceiling` (the default `small` on a plan whose ceiling sits below it) is granted clamped to the ceiling when the create names no size or marks the size advisory, and refused with RESOURCES_EXCEED_LIMIT when the create names that preset's numbers explicitly. `default` names the platform's default preset: a create that sends no `resources` receives that preset's CPU and memory, but its disk is currently held at 2 GiB (the host warm-seed quota) rather than the preset's, so name `resources.diskGB` when you need more.","responses":{"200":{"description":"Shapes retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxShapesResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/shapes`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/shapes`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/shapes\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/shapes\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/signup":{"post":{"tags":["Account"],"security":[],"summary":"Create an account without sign-in","description":"Needs no credential and is off in production, which answers 410 SIGNUP_DISABLED: sign in through the OAuth provider instead. Where a deployment turns it on, the account is created unverified and no API key is issued.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","minLength":3},"name":{"type":"string","description":"Trimmed; 1-100 characters without < or > or control characters. A blank name is ignored."}},"required":["email"]}}}},"responses":{"201":{"description":"The account was created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"customer_id":{"type":"string"},"email":{"type":"string"}},"required":["customer_id","email"]}},"required":["success","data"]}}}},"400":{"description":"The email or name is invalid (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"409":{"description":"The email is already registered (EMAIL_EXISTS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}},"410":{"description":"Direct sign-up is off on this deployment (SIGNUP_DISABLED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FailureEnvelope"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/signup`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/signup`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/signup\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/signup\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/ssh-keys":{"get":{"tags":["SSH keys"],"summary":"List SSH keys","responses":{"200":{"description":"List SSH keys","content":{"application/json":{"schema":{"type":"object","properties":{"sshKeys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"publicKey":{"type":"string"},"fingerprint":{"type":"string"},"keyType":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","publicKey","fingerprint","keyType","createdAt","updatedAt"]}}},"required":["sshKeys"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/ssh-keys`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/ssh-keys`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/ssh-keys\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/ssh-keys\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["SSH keys"],"summary":"Add an SSH key","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"publicKey":{"type":"string","minLength":1,"maxLength":4096}},"required":["name","publicKey"]}}}},"responses":{"201":{"description":"SSH key created","content":{"application/json":{"schema":{"type":"object","properties":{"sshKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"publicKey":{"type":"string"},"fingerprint":{"type":"string"},"keyType":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","publicKey","fingerprint","keyType","createdAt","updatedAt"]}},"required":["sshKey"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/ssh-keys`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/ssh-keys`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/ssh-keys\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/ssh-keys\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/ssh-keys/{id}":{"delete":{"tags":["SSH keys"],"summary":"Delete an SSH key","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"SSH key deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/ssh-keys/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/ssh-keys/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/ssh-keys/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/ssh-keys/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/startup-scripts":{"get":{"tags":["Startup scripts"],"summary":"List startup scripts","responses":{"200":{"description":"List of startup scripts","content":{"application/json":{"schema":{"type":"object","properties":{"scripts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string"},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"]},"minRamGB":{"type":["number","null"]},"runOrder":{"type":"number"},"timeoutSeconds":{"type":"number"},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","scriptType","content","environments","minCpuCores","minRamGB","runOrder","timeoutSeconds","continueOnFailure","runAsRoot","injectSecrets","enabled","createdAt","updatedAt"]}}},"required":["scripts"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/startup-scripts`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/startup-scripts`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/startup-scripts\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Startup scripts"],"summary":"Create a startup script","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":256,"default":""},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"],"default":"bash"},"content":{"type":"string","minLength":1,"maxLength":65536},"environments":{"type":"array","items":{"type":"string"},"default":[]},"minCpuCores":{"type":["number","null"],"minimum":0.5,"default":null},"minRamGB":{"type":["number","null"],"minimum":1,"default":null},"runOrder":{"type":"integer","minimum":0,"maximum":9999,"default":100},"timeoutSeconds":{"type":"integer","minimum":5,"maximum":600,"default":300},"continueOnFailure":{"type":"boolean","default":false},"runAsRoot":{"type":"boolean","default":false},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50,"default":[]},"enabled":{"type":"boolean","default":true}},"required":["name","content"]}}}},"responses":{"201":{"description":"Script created","content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string"},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"]},"minRamGB":{"type":["number","null"]},"runOrder":{"type":"number"},"timeoutSeconds":{"type":"number"},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","scriptType","content","environments","minCpuCores","minRamGB","runOrder","timeoutSeconds","continueOnFailure","runAsRoot","injectSecrets","enabled","createdAt","updatedAt"]}},"required":["script"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Tier restriction","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/startup-scripts`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/startup-scripts`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/startup-scripts\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/startup-scripts/{id}":{"get":{"tags":["Startup scripts"],"summary":"Get a startup script","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Script details","content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string"},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"]},"minRamGB":{"type":["number","null"]},"runOrder":{"type":"number"},"timeoutSeconds":{"type":"number"},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","scriptType","content","environments","minCpuCores","minRamGB","runOrder","timeoutSeconds","continueOnFailure","runAsRoot","injectSecrets","enabled","createdAt","updatedAt"]}},"required":["script"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/startup-scripts/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"put":{"tags":["Startup scripts"],"summary":"Update a startup script","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":256},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string","minLength":1,"maxLength":65536},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"],"minimum":0.5},"minRamGB":{"type":["number","null"],"minimum":1},"runOrder":{"type":"integer","minimum":0,"maximum":9999},"timeoutSeconds":{"type":"integer","minimum":5,"maximum":600},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Script updated","content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string"},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"]},"minRamGB":{"type":["number","null"]},"runOrder":{"type":"number"},"timeoutSeconds":{"type":"number"},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","scriptType","content","environments","minCpuCores","minRamGB","runOrder","timeoutSeconds","continueOnFailure","runAsRoot","injectSecrets","enabled","createdAt","updatedAt"]}},"required":["script"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Tier restriction","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/startup-scripts/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts/${SANDBOX_ID:?Set SANDBOX_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Startup scripts"],"summary":"Delete a startup script","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Script deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/startup-scripts/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/startup-scripts/{id}/toggle":{"patch":{"tags":["Startup scripts"],"summary":"Toggle a startup script enabled/disabled","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Script toggled","content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"scriptType":{"type":"string","enum":["bash","python","node","ruby","custom"]},"content":{"type":"string"},"environments":{"type":"array","items":{"type":"string"}},"minCpuCores":{"type":["number","null"]},"minRamGB":{"type":["number","null"]},"runOrder":{"type":"number"},"timeoutSeconds":{"type":"number"},"continueOnFailure":{"type":"boolean"},"runAsRoot":{"type":"boolean"},"injectSecrets":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"maxItems":50},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","scriptType","content","environments","minCpuCores","minRamGB","runOrder","timeoutSeconds","continueOnFailure","runAsRoot","injectSecrets","enabled","createdAt","updatedAt"]}},"required":["script"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}/toggle`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"PATCH\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/startup-scripts/${encodeURIComponent(id)}/toggle`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"PATCH\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/startup-scripts/{quote(_id or '', safe='')}/toggle\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/startup-scripts/${SANDBOX_ID:?Set SANDBOX_ID}/toggle\"\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams":{"get":{"tags":["Teams"],"summary":"List teams the current user belongs to","responses":{"200":{"description":"Teams list","content":{"application/json":{"schema":{"type":"object","properties":{"teams":{"type":"array","items":{"$ref":"#/components/schemas/Team"}}},"required":["teams"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/teams`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/teams`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/teams\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Teams"],"summary":"Create a team owned by the current user","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"orgId":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"Team created","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"}},"required":["team"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/teams`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/teams`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/teams\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/teams/invitations/{id}":{"delete":{"tags":["Teams"],"summary":"Revoke a pending invitation","parameters":[{"schema":{"type":"string","pattern":"^tinv_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"No longer pending","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/invitations/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/invitations/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/invitations/{token}":{"get":{"tags":["Teams"],"summary":"Preview an invitation (team name + inviter) by token","parameters":[{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Invitation preview","content":{"application/json":{"schema":{"type":"object","properties":{"invitation":{"type":"object","properties":{"email":{"type":"string"},"role":{"type":"string","enum":["admin","member","viewer"]},"status":{"type":"string","enum":["pending","accepted","expired","revoked"]},"expiresAt":{"type":"string"},"teamName":{"type":"string"},"invitedByEmail":{"type":["string","null"]}},"required":["email","role","status","expiresAt","teamName","invitedByEmail"]}},"required":["invitation"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst token = process.env.SANDBOX_TOKEN;\nif (!token) throw new Error(\"Set SANDBOX_TOKEN\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(token)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst token = process.env.SANDBOX_TOKEN;\nif (!token) throw new Error(\"Set SANDBOX_TOKEN\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(token)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_token = os.environ.get(\"SANDBOX_TOKEN\")\nif not _token: raise SystemExit(\"Set SANDBOX_TOKEN\")\nurl = f\"{base_url}/v1/teams/invitations/{quote(_token or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/invitations/${SANDBOX_TOKEN:?Set SANDBOX_TOKEN}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/invitations/{token}/accept":{"post":{"tags":["Teams"],"summary":"Accept a team invitation","parameters":[{"schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/TeamMember"}},"required":["member"]}}}},"403":{"description":"Email mismatch","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Invitation not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Invitation terminal state","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst token = process.env.SANDBOX_TOKEN;\nif (!token) throw new Error(\"Set SANDBOX_TOKEN\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(token)}/accept`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst token = process.env.SANDBOX_TOKEN;\nif (!token) throw new Error(\"Set SANDBOX_TOKEN\");\nconst url = new URL(`/v1/teams/invitations/${encodeURIComponent(token)}/accept`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_token = os.environ.get(\"SANDBOX_TOKEN\")\nif not _token: raise SystemExit(\"Set SANDBOX_TOKEN\")\nurl = f\"{base_url}/v1/teams/invitations/{quote(_token or '', safe='')}/accept\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/invitations/${SANDBOX_TOKEN:?Set SANDBOX_TOKEN}/accept\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}":{"get":{"tags":["Teams"],"summary":"Get a team the user belongs to","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Team","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"}},"required":["team"]}}}},"403":{"description":"Not a team member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"patch":{"tags":["Teams"],"summary":"Update team metadata","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"}},"required":["team"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Teams"],"summary":"Delete a team (owner only)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/egress":{"get":{"summary":"Get team egress policy","description":"Returns the team's default egress policy, or null if none is set.","tags":["Teams"],"parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Egress policy retrieved","content":{"application/json":{"schema":{"type":"object","properties":{"policy":{"type":["object","null"],"properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false}},"required":["policy"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Team not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/egress\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/egress\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"put":{"summary":"Set team egress policy","description":"Set or update the team's default egress policy. Only owners and admins.","tags":["Teams"],"parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["open","strict","blocked"]},"allowDomains":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":256},{"type":"object","properties":{"domain":{"type":"string","minLength":1,"maxLength":256},"allowPrivateIps":{"type":"array","items":{"anyOf":[{"type":"string","format":"ipv4-cidr"},{"type":"string","format":"ipv6-cidr"}]},"minItems":1,"maxItems":100}},"required":["domain","allowPrivateIps"],"additionalProperties":false}]},"maxItems":100},"includeImplicitDomains":{"type":"boolean"}},"required":["mode"],"additionalProperties":false}}}},"responses":{"200":{"description":"Policy updated","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"}},"required":["team"]}}}},"400":{"description":"Invalid policy","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Team not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/egress\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/egress\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"summary":"Clear team egress policy","description":"Remove the team's custom egress policy. Sandboxes will fall back to platform defaults.","tags":["Teams"],"parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Policy cleared","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"}},"required":["team"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Team not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/egress`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/egress\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/egress\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/invitations":{"get":{"tags":["Teams"],"summary":"List invitations for the team","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Invitations","content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/TeamInvitation"}}},"required":["invitations"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/invitations`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/invitations`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/invitations\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/invitations\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Teams"],"summary":"Invite a user to the team by email","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member","viewer"]},"ttlHours":{"type":"integer","minimum":1,"maximum":720}},"required":["email","role"]}}}},"responses":{"201":{"description":"Invitation created","content":{"application/json":{"schema":{"type":"object","properties":{"invitation":{"$ref":"#/components/schemas/TeamInvitation"}},"required":["invitation"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Duplicate or existing member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/invitations`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/invitations`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/invitations\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/invitations\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/teams/{id}/leave":{"post":{"tags":["Teams"],"summary":"Leave a team (non-owner members only)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Left","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/leave`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/leave`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/leave\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/leave\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/members":{"get":{"tags":["Teams"],"summary":"List team members","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Member list","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}},"required":["members"]}}}},"403":{"description":"Not a team member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/members\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/members\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/members/{memberId}":{"patch":{"tags":["Teams"],"summary":"Update a team member's role or status","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"pattern":"^tm_[a-f0-9]{32}$"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","member","viewer"]},"status":{"type":"string","enum":["active","removed"]}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/TeamMember"}},"required":["member"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PATCH\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/members/{quote(_memberId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PATCH\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PATCH \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Teams"],"summary":"Remove a team member","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"pattern":"^tm_[a-f0-9]{32}$"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst memberId = process.env.SANDBOX_MEMBER_ID;\nif (!memberId) throw new Error(\"Set SANDBOX_MEMBER_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_memberId = os.environ.get(\"SANDBOX_MEMBER_ID\")\nif not _memberId: raise SystemExit(\"Set SANDBOX_MEMBER_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/members/{quote(_memberId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/members/${SANDBOX_MEMBER_ID:?Set SANDBOX_MEMBER_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/secrets":{"get":{"tags":["Teams"],"summary":"List team secret names (values never returned)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Secret list","content":{"application/json":{"schema":{"type":"object","properties":{"secrets":{"type":"array","items":{"$ref":"#/components/schemas/TeamSecret"}}},"required":["secrets"]}}}},"403":{"description":"Not a team member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team secrets not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/secrets\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/secrets\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/secrets/{name}":{"put":{"tags":["Teams"],"summary":"Create or replace a team secret","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"required":true,"name":"name","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":65536},"hosts":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"minItems":1,"maxItems":8},"header":{"type":"string"},"exposeInBox":{"type":"boolean","enum":[true]},"proxySecrets":{"type":"array","items":{"type":"object","properties":{"hosts":{"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$"},"minItems":1,"maxItems":1024},"methods":{"type":"array","items":{"type":"string","maxLength":32,"pattern":"^[A-Z]+$"},"minItems":1,"maxItems":1024},"paths":{"type":"array","items":{"type":"string","maxLength":2048},"minItems":1,"maxItems":1024},"envName":{"type":"string","maxLength":256,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"secretRef":{"type":"object","properties":{"kind":{"type":"string","enum":["secret-ref"]},"key":{"type":"string","minLength":1,"maxLength":500}},"required":["kind","key"],"additionalProperties":false},"presentation":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["bearer"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["basic"]},"username":{"type":"string","minLength":1,"maxLength":256,"pattern":"^[\\x21-\\x39\\x3b-\\x7e]+$"}},"required":["type","username"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["header"]},"name":{"type":"string","maxLength":256,"pattern":"^[!#$%&'*+.^_\\x60|~0-9A-Za-z-]+$"}},"required":["type","name"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"name":{"type":"string","maxLength":256,"pattern":"^[A-Za-z0-9._~-]+$"}},"required":["type","name"],"additionalProperties":false}]},"placeholderFormat":{"type":"string","enum":["opaque","jwt"]},"ttlSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["hosts","envName","secretRef","presentation"],"additionalProperties":false},"maxItems":1024},"reason":{"type":"string"}},"required":["value"],"additionalProperties":false}}}},"responses":{"200":{"description":"Upserted","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"$ref":"#/components/schemas/TeamSecret"}},"required":["secret"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team secrets not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"503":{"description":"Live re-key not acknowledged","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"PUT\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_name = os.environ.get(\"SANDBOX_NAME\")\nif not _name: raise SystemExit(\"Set SANDBOX_NAME\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/secrets/{quote(_name or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"PUT\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/secrets/${SANDBOX_NAME:?Set SANDBOX_NAME}\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request PUT \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]},"delete":{"tags":["Teams"],"summary":"Delete a team secret","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team secrets not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_name = os.environ.get(\"SANDBOX_NAME\")\nif not _name: raise SystemExit(\"Set SANDBOX_NAME\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/secrets/{quote(_name or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/secrets/${SANDBOX_NAME:?Set SANDBOX_NAME}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/secrets/{name}/reveal":{"post":{"tags":["Teams"],"summary":"Reveal the plaintext value (owner/admin only, audit-logged)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":500},{"type":"string","maxLength":64,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}]},"required":true,"name":"name","in":"path"}],"responses":{"200":{"description":"Plaintext value","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"required":["name","value"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team secrets not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}/reveal`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst name = process.env.SANDBOX_NAME;\nif (!name) throw new Error(\"Set SANDBOX_NAME\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/secrets/${encodeURIComponent(name)}/reveal`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"POST\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_name = os.environ.get(\"SANDBOX_NAME\")\nif not _name: raise SystemExit(\"Set SANDBOX_NAME\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/secrets/{quote(_name or '', safe='')}/reveal\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/secrets/${SANDBOX_NAME:?Set SANDBOX_NAME}/reveal\"\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/templates":{"get":{"tags":["Teams"],"summary":"List team golden-path templates","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template list","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/TeamTemplate"}}},"required":["templates"]}}}},"403":{"description":"Not a team member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team templates not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/templates\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Teams"],"summary":"Create a team golden-path template (owner/admin only)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":256,"default":""},"snapshotId":{"type":"string","minLength":1},"sourceSandboxId":{"type":"string","minLength":1},"environment":{"type":"string","default":"universal"},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["name","snapshotId","sourceSandboxId"]}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/TeamTemplate"}},"required":["template"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team templates not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/templates\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/teams/{id}/templates/{templateId}":{"delete":{"tags":["Teams"],"summary":"Delete a team golden-path template (owner/admin only)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"templateId","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"403":{"description":"Insufficient permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"501":{"description":"Team templates not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst templateId = process.env.SANDBOX_TEMPLATE_ID;\nif (!templateId) throw new Error(\"Set SANDBOX_TEMPLATE_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates/${encodeURIComponent(templateId)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst templateId = process.env.SANDBOX_TEMPLATE_ID;\nif (!templateId) throw new Error(\"Set SANDBOX_TEMPLATE_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/templates/${encodeURIComponent(templateId)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\n_templateId = os.environ.get(\"SANDBOX_TEMPLATE_ID\")\nif not _templateId: raise SystemExit(\"Set SANDBOX_TEMPLATE_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/templates/{quote(_templateId or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/templates/${SANDBOX_TEMPLATE_ID:?Set SANDBOX_TEMPLATE_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/teams/{id}/transfer":{"post":{"tags":["Teams"],"summary":"Transfer team ownership (current owner only)","parameters":[{"schema":{"type":"string","minLength":1,"pattern":"^team_[a-f0-9]{32}$"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"newOwnerCustomerId":{"type":"string","minLength":1,"maxLength":100}},"required":["newOwnerCustomerId"]}}}},"responses":{"200":{"description":"Ownership transferred (no content)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"403":{"description":"Not the current owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"404":{"description":"Target not a member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/transfer`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/teams/${encodeURIComponent(id)}/transfer`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/teams/{quote(_id or '', safe='')}/transfer\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/teams/${SANDBOX_ID:?Set SANDBOX_ID}/transfer\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/templates":{"get":{"tags":["Templates"],"summary":"List user templates","responses":{"200":{"description":"List of templates","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"snapshotId":{"type":"string"},"sourceSandboxId":{"type":["string","null"]},"environment":{"type":"string"},"config":{"type":"object","additionalProperties":{}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","snapshotId","sourceSandboxId","environment","config","createdAt","updatedAt"]}}},"required":["templates"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/templates\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]},"post":{"tags":["Templates"],"summary":"Create a template from a snapshot","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":256,"default":""},"snapshotId":{"type":"string","minLength":1},"sourceSandboxId":{"type":"string","minLength":1},"environment":{"type":"string","default":"universal"},"config":{"type":"object","additionalProperties":{},"default":{}}},"required":["name","snapshotId","sourceSandboxId"]}}}},"responses":{"201":{"description":"Template created","content":{"application/json":{"schema":{"type":"object","properties":{"template":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"snapshotId":{"type":"string"},"sourceSandboxId":{"type":["string","null"]},"environment":{"type":"string"},"config":{"type":"object","additionalProperties":{}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","snapshotId","sourceSandboxId","environment","config","createdAt","updatedAt"]}},"required":["template"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"409":{"description":"Template already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\n// Run with Node.js 22+ and the project’s TypeScript runner.\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/templates`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"import { readFile } from \"node:fs/promises\";\nconst baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/templates`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\n// Save a request body that matches the schema above as request.json.\nconst body = await readFile(process.env.SANDBOX_REQUEST_FILE ?? \"request.json\", \"utf8\");\nheaders[\"Content-Type\"] = \"application/json\";\nconst response = await fetch(url, { method: \"POST\", headers, body });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/templates\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\n# Save a request body matching the schema above as request.json.\nwith open(os.environ.get(\"SANDBOX_REQUEST_FILE\", \"request.json\"), \"rb\") as request_file:\n    body = request_file.read()\nheaders[\"Content-Type\"] = \"application/json\"\nrequest = Request(url, data=body, headers=headers, method=\"POST\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/templates\"\n# SANDBOX_REQUEST_FILE must match the request-body schema above.\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\" \\\n  --header \"Content-Type: application/json\" \\\n  --data-binary \"@${SANDBOX_REQUEST_FILE:-request.json}\""}]}},"/v1/templates/{id}":{"delete":{"tags":["Templates"],"summary":"Delete a template","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Template deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"nextStep":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["message","code"]}},"required":["error"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/templates/${encodeURIComponent(id)}`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst id = process.env.SANDBOX_ID;\nif (!id) throw new Error(\"Set SANDBOX_ID\");\nconst url = new URL(`/v1/templates/${encodeURIComponent(id)}`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"DELETE\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\n_id = os.environ.get(\"SANDBOX_ID\")\nif not _id: raise SystemExit(\"Set SANDBOX_ID\")\nurl = f\"{base_url}/v1/templates/{quote(_id or '', safe='')}\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"DELETE\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/templates/${SANDBOX_ID:?Set SANDBOX_ID}\"\ncurl --fail-with-body --silent --show-error \\\n  --request DELETE \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/usage":{"get":{"summary":"Get current usage stats","tags":["Usage"],"responses":{"200":{"description":"Current usage","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/usage`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/usage`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/usage\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/usage\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/usage/egress":{"get":{"summary":"Get sandbox egress for a calendar month","description":"The account's egress for one calendar month (UTC) as the meter recorded it: the account total, the plan's allowance, the overage beyond it, each sandbox's bytes, and what the meter has charged or reserved. Paid plans include 2 TB per account per month, shared by its sandboxes, then bill per decimal GB; Free accounts are capped at 100 GiB per month and never charged.","tags":["Usage"],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Calendar month, `YYYY-MM` in UTC. Defaults to the current month.","example":"2026-10"},"required":false,"description":"Calendar month, `YYYY-MM` in UTC. Defaults to the current month.","name":"month","in":"query"}],"responses":{"200":{"description":"Egress for the month","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/EgressUsage"}},"required":["success","data"]}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/usage/egress`, baseUrl);\nconst month = process.env.SANDBOX_MONTH;\nif (month !== undefined) url.searchParams.set(\"month\", month);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/usage/egress`, baseUrl);\nconst month = process.env.SANDBOX_MONTH;\nif (month !== undefined) url.searchParams.set(\"month\", month);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/usage/egress\"\nquery = {}\n_month = os.environ.get(\"SANDBOX_MONTH\")\nif _month is not None: query[\"month\"] = _month\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/usage/egress\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_MONTH:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}month=${SANDBOX_MONTH}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/usage/export":{"get":{"summary":"Export usage data as CSV","tags":["Usage"],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":90,"default":30},"required":false,"name":"days","in":"query"}],"responses":{"200":{"description":"CSV file download","content":{"text/csv":{"schema":{"type":"string"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/usage/export`, baseUrl);\nconst days = process.env.SANDBOX_DAYS;\nif (days !== undefined) url.searchParams.set(\"days\", days);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/usage/export`, baseUrl);\nconst days = process.env.SANDBOX_DAYS;\nif (days !== undefined) url.searchParams.set(\"days\", days);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/usage/export\"\nquery = {}\n_days = os.environ.get(\"SANDBOX_DAYS\")\nif _days is not None: query[\"days\"] = _days\nif query: url += \"?\" + urlencode(query, doseq=True)\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/usage/export\"\n# URL-encode each query value before exporting the parameter.\nif [ -n \"${SANDBOX_DAYS:-}\" ]; then\n  separator='?'; case \"$url\" in *\\?*) separator='&' ;; esac; url=\"$url${separator}days=${SANDBOX_DAYS}\"\nfi\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/v1/usage/subscription":{"get":{"summary":"Get subscription info","tags":["Usage"],"responses":{"200":{"description":"Subscription info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResponse"}}}},"404":{"description":"No subscription found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/v1/usage/subscription`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/v1/usage/subscription`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/v1/usage/subscription\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/v1/usage/subscription\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/version":{"get":{"tags":["Service"],"security":[],"summary":"Get the deployed revision","description":"Needs no credential. The response is never cached. A 503 means the deploy stamp names a different revision than the running bundle.","responses":{"200":{"description":"Revision of the running bundle","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string","description":"Git revision of the running bundle."},"buildTime":{"type":"string"},"d1EgressRunBaselineFinalized":{"type":"boolean","enum":[true]},"workerVersionId":{"type":"string"}},"required":["version","buildTime"]}}}},"503":{"description":"Deploy stamp mismatch","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["DEPLOY_STAMP_MISMATCH"]},"bundleRevision":{"type":"string"},"deployVariable":{"type":"string"}},"required":["error","code","bundleRevision","deployVariable"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/version`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/version`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/version\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/version\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}},"/version/":{"get":{"tags":["Service"],"security":[],"summary":"Get the deployed revision (trailing slash)","description":"Needs no credential. The response is never cached. A 503 means the deploy stamp names a different revision than the running bundle.","responses":{"200":{"description":"Revision of the running bundle","content":{"application/json":{"schema":{"type":"object","properties":{"version":{"type":"string","description":"Git revision of the running bundle."},"buildTime":{"type":"string"},"d1EgressRunBaselineFinalized":{"type":"boolean","enum":[true]},"workerVersionId":{"type":"string"}},"required":["version","buildTime"]}}}},"503":{"description":"Deploy stamp mismatch","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["DEPLOY_STAMP_MISMATCH"]},"bundleRevision":{"type":"string"},"deployVariable":{"type":"string"}},"required":["error","code","bundleRevision","deployVariable"]}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"TypeScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\n// Run with Node.js 22+ and the project’s TypeScript runner.\nconst url = new URL(`/version/`, baseUrl);\nconst headers: Record<string, string> = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"JavaScript","label":"JavaScript · fetch","source":"const baseUrl = process.env.SANDBOX_BASE_URL ?? \"https://sandbox.tangle.tools\";\nconst apiKey = process.env.TANGLE_API_KEY;\nif (!apiKey) throw new Error(\"Set TANGLE_API_KEY in a trusted runtime\");\nconst url = new URL(`/version/`, baseUrl);\nconst headers = { Authorization: `Bearer ${apiKey}`, Accept: \"*/*\" };\nconst response = await fetch(url, { method: \"GET\", headers });\nif (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);\nconst reader = response.body?.getReader();\nif (reader) {\n  const decoder = new TextDecoder();\n  while (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    process.stdout.write(decoder.decode(value, { stream: true }));\n  }\n  process.stdout.write(decoder.decode());\n} else {\n  process.stdout.write(await response.text());\n}"},{"lang":"Python","label":"Python · urllib","source":"import os\nimport sys\nfrom urllib.error import HTTPError\nfrom urllib.parse import quote, urlencode\nfrom urllib.request import Request, urlopen\n\napi_key = os.environ.get(\"TANGLE_API_KEY\")\nif not api_key: raise SystemExit(\"Set TANGLE_API_KEY in a trusted runtime\")\nbase_url = os.environ.get(\"SANDBOX_BASE_URL\", \"https://sandbox.tangle.tools\").rstrip(\"/\")\nurl = f\"{base_url}/version/\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\", \"Accept\": \"*/*\"}\nrequest = Request(url, data=None, headers=headers, method=\"GET\")\ntry:\n    with urlopen(request, timeout=float(os.environ.get(\"SANDBOX_TIMEOUT_SECONDS\", \"300\"))) as response:\n        while chunk := response.read(8192):\n            sys.stdout.buffer.write(chunk)\nexcept HTTPError as error:\n    detail = error.read().decode(\"utf-8\", errors=\"replace\")\n    raise SystemExit(f\"HTTP {error.code}: {detail}\") from error"},{"lang":"Shell","label":"cURL","source":"# Store TANGLE_API_KEY in a trusted shell; never paste it into browser code.\nbase_url=\"${SANDBOX_BASE_URL:-https://sandbox.tangle.tools}\"\nbase_url=\"${base_url%/}\"\nurl=\"$base_url/version/\"\ncurl --fail-with-body --silent --show-error \\\n  --request GET \\\n  --url \"$url\" \\\n  --header \"Authorization: Bearer ${TANGLE_API_KEY:?Set TANGLE_API_KEY}\" \\\n  --header \"Accept: */*\""}]}}},"webhooks":{},"tags":[{"name":"Sandboxes"},{"name":"Sessions"},{"name":"Fleets"},{"name":"GPU"},{"name":"Durable plans"},{"name":"Templates"},{"name":"Public templates"},{"name":"Images"},{"name":"Instances"},{"name":"Secrets"},{"name":"SSH keys"},{"name":"Startup scripts"},{"name":"Lines"},{"name":"Teams"},{"name":"Account"},{"name":"Sign-in"},{"name":"Usage"},{"name":"Billing"},{"name":"Pricing"},{"name":"Notifications"},{"name":"Backends"},{"name":"Intelligence"},{"name":"E2B compatibility"},{"name":"Service"}],"servers":[{"url":"https://sandbox.tangle.tools","description":"Tangle Sandbox API"}],"security":[{"BearerAuth":[]}],"x-tangle-contract-revision":"sha256:4eec765898237f8e836d796babc91000b1f4b3d3715459a6064fd608aab050dd"}