{"openapi":"3.1.0","info":{"title":"IG1 Cloud API","description":"Unified API gateway for IG1 Private Cloud (OpenStack + Kubernetes)","version":"0.38.0","x-response-examples-injected":48,"x-ig1-plane":"customer"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Health","description":"Aggregate health at GET /health (fixes the skeleton's 404).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"status":"ok"}}}}}}},"/health/live":{"get":{"tags":["health"],"summary":"Liveness","operationId":"liveness_health_live_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"status":"ok"}}}}}}},"/health/ready":{"get":{"tags":["health"],"summary":"Readiness","description":"Report upstream reachability; ALWAYS 200 while this pod can serve.\n\nSee the module docstring: this is the kubelet's readinessProbe, so a 503\nhere removes the pod from its Service. Upstream state belongs in the body.","operationId":"readiness_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"status":"ok"}}}}}}},"/v1/openstack/{path}":{"delete":{"tags":["openstack"],"summary":"Proxy Openstack","description":"Forward to OpenStack services.\n\nFirst path segment selects the service (identity/compute/network/volume/\nimage/dns/load-balancer/key-manager); the rest is appended to that\nservice's versioned base URL.","operationId":"proxy_openstack_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"networks":[{"id":"8c4b1e07-52a9-4d3f-a6c1-19e7b0d4f2a3","name":"provider-network","project_id":"00000000000000000000000000000001","status":"ACTIVE","admin_state_up":true,"subnets":["b41d9f2e-6c07-4a58-9d13-2f8ab5c604e7"],"router:external":true,"mtu":1442}],"//":"verbatim from the upstream service — shape depends on the path"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["openstack"],"summary":"Proxy Openstack","description":"Forward to OpenStack services.\n\nFirst path segment selects the service (identity/compute/network/volume/\nimage/dns/load-balancer/key-manager); the rest is appended to that\nservice's versioned base URL.","operationId":"proxy_openstack_patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"networks":[{"id":"8c4b1e07-52a9-4d3f-a6c1-19e7b0d4f2a3","name":"provider-network","project_id":"00000000000000000000000000000001","status":"ACTIVE","admin_state_up":true,"subnets":["b41d9f2e-6c07-4a58-9d13-2f8ab5c604e7"],"router:external":true,"mtu":1442}],"//":"verbatim from the upstream service — shape depends on the path"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["openstack"],"summary":"Proxy Openstack","description":"Forward to OpenStack services.\n\nFirst path segment selects the service (identity/compute/network/volume/\nimage/dns/load-balancer/key-manager); the rest is appended to that\nservice's versioned base URL.","operationId":"proxy_openstack_put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"networks":[{"id":"8c4b1e07-52a9-4d3f-a6c1-19e7b0d4f2a3","name":"provider-network","project_id":"00000000000000000000000000000001","status":"ACTIVE","admin_state_up":true,"subnets":["b41d9f2e-6c07-4a58-9d13-2f8ab5c604e7"],"router:external":true,"mtu":1442}],"//":"verbatim from the upstream service — shape depends on the path"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["openstack"],"summary":"Proxy Openstack","description":"Forward to OpenStack services.\n\nFirst path segment selects the service (identity/compute/network/volume/\nimage/dns/load-balancer/key-manager); the rest is appended to that\nservice's versioned base URL.","operationId":"proxy_openstack_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"networks":[{"id":"8c4b1e07-52a9-4d3f-a6c1-19e7b0d4f2a3","name":"provider-network","project_id":"00000000000000000000000000000001","status":"ACTIVE","admin_state_up":true,"subnets":["b41d9f2e-6c07-4a58-9d13-2f8ab5c604e7"],"router:external":true,"mtu":1442}],"//":"verbatim from the upstream service — shape depends on the path"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["openstack"],"summary":"Proxy Openstack","description":"Forward to OpenStack services.\n\nFirst path segment selects the service (identity/compute/network/volume/\nimage/dns/load-balancer/key-manager); the rest is appended to that\nservice's versioned base URL.","operationId":"proxy_openstack_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"networks":[{"id":"8c4b1e07-52a9-4d3f-a6c1-19e7b0d4f2a3","name":"provider-network","project_id":"00000000000000000000000000000001","status":"ACTIVE","admin_state_up":true,"subnets":["b41d9f2e-6c07-4a58-9d13-2f8ab5c604e7"],"router:external":true,"mtu":1442}],"//":"verbatim from the upstream service — shape depends on the path"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/credentials":{"post":{"tags":["credentials"],"summary":"Create Credential","description":"Mint a scoped credential: Zitadel machine user (Management API) +\ndynamic-store entry + paste-ready PAT. The client_secret and the PAT\nare in THIS response only.","operationId":"create_credential_v1_credentials_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"credentials":[]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["credentials"],"summary":"List Credentials","description":"Caller-visible credential entries (admin: all; customer: own project\nonly). Never carries secrets — they were never stored.\n\nRevoked entries are excluded by default (2026-08-06): the store keeps\nthem as the audit trail, but every live consumer (portal, CLI, the\nvalidators' convergence polls) wants the ACTIVE set — and hundreds of\naccumulated revoked entries made the payload large enough to SIGPIPE\n`head -1` extraction in the validators. Pass ?include_revoked=true for\nthe audit view (the store GC stays the W9 hardening item).","operationId":"list_credentials_v1_credentials_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include_revoked","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Revoked"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"credentials":[]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/credentials/{credential_id}":{"delete":{"tags":["credentials"],"summary":"Revoke Credential","description":"Deactivate the machine user + mark the store entry revoked. A\ncross-tenant id answers 404 (existence is not leaked to other tenants).","operationId":"revoke_credential_v1_credentials__credential_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"credential_id","in":"path","required":true,"schema":{"type":"string","title":"Credential Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/credentials/usage":{"get":{"tags":["credentials"],"summary":"Credentials Usage","description":"Per-credential last-used + call count, read from the topics that record\nCALLS.\n\n2026-08-22 — this counted the wrong thing, and a customer noticed. It read\n``iam.credentials``, which is the MINT/REVOKE trail: creating a credential\nemits ``credential.created``, so a brand-new credential was immediately\nreported as having been used. The platform's owner created two credentials\nand the console showed \"2 calls\" against each of them before either had ever\nauthenticated anything.\n\nIt was also nondeterministic. ``_audit_ring`` is a per-process ring, so the\ntwo api replicas hold different halves of the same history, and the same\nmint reached the tally twice — once from the local ring, once from the bus —\nwith the total depending on which replica answered.\n\nThe topics that record actual calls are:\n\n  · ``api.audit``    — one event per completed write on the IG1 API, keyed\n                       directly by ``credential_id`` (schemas.py);\n  · ``agent.actions``— one event per MCP tool call, keyed by\n                       ``actor_user_id``, which is the credential's Zitadel\n                       machine user (``CredEntry.zitadel_user_id``).\n\nBoth are read from the bus only. The local ring is NOT merged in: it holds\nmanagement events, it is per-replica, and merging it is what made the count\ndepend on which pod answered. Dedupe is by envelope id, because the bus can\nreplay and because a caller polling twice must not double a count.\n\nHONEST LIMIT, and the console says it rather than implying otherwise: reads\nare not audited (``api.audit`` is writes only, by design — the topic's own\ndocstring says so), so this is a WRITE count, not a request count. A\nread-only tier-0 credential doing its job correctly will show zero here for\never, and that is the true answer, not a broken one.","operationId":"credentials_usage_v1_credentials_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":7,"usage":[{"call_count":1,"credential_id":"cred-5004dad0b3b74d56","label":"pytest26-t0-9b4d9288","last_used_at":"2026-08-23T03:22:06Z","project_id":"00000000000000000000000000000001"},{"call_count":2,"credential_id":"cred-8c85c44b9bd448a6","label":"pytest26-t1-ea0e55d3","last_used_at":"2026-08-23T03:24:23Z","project_id":"00000000000000000000000000000001"},{"call_count":1,"credential_id":"cred-b4af6094d7494290","label":"pytest26-t0-8a75b7d9","last_used_at":"2026-08-23T03:46:06Z","project_id":"00000000000000000000000000000001"},{"call_count":2,"credential_id":"cred-d1709664d3fb4369","label":"pytest26-t1-325c2fd5","last_used_at":"2026-08-23T03:46:56Z","project_id":"00000000000000000000000000000001"},{"call_count":3,"credential_id":"cred-80127f7956df44e4","label":"val33-t0-1787458746","last_used_at":"2026-08-23T04:21:18Z","project_id":"00000000000000000000000000000001"},{"call_count":4,"credential_id":"cred-0d2191538d7a4441","label":"val33-t1-1787458746","last_used_at":"2026-08-23T04:21:22Z","project_id":"00000000000000000000000000000001"},{"call_count":3,"credential_id":"cred-0b76672b191b49dd","label":"val33-t2-1787458746","last_used_at":"2026-08-23T04:21:37Z","project_id":"00000000000000000000000000000001"}]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/org/users":{"get":{"tags":["credentials"],"summary":"List Org Users","description":"Human users with their PoC project roles, scoped to ONE account.\nAdmin and customer-admin only.\n\nW9/B (2026-08-08 audit §2.4): the org header alone was never isolation —\nevery tenant shares one Zitadel org, so this listed every customer's\nusers to every customer-admin. Ownership (_user_projects) is the\nboundary, not the header.\n\n2026-09-22 — THE PLATFORM VIEW IS NO LONGER THE DEFAULT. A platform\nadmin asking this endpoint with no arguments used to receive every human\nof every tenant in one flat list, with nothing on a row saying which\naccount the person belonged to. That answered a question nobody asked:\n\"Organization members\" means the members of ONE organisation, and a\ntwenty-tenant list under that heading is not a scoped view that happens\nto be wide — it is a different surface wearing the wrong name, and the\nbanner apologising for it said as much.\n\nThe three readings are now asked for by name:\n\n  * no arguments — the CALLER'S account. A platform credential is scoped\n    to no tenant (auth.tenant_project), so what it owns is the set of\n    humans bound to no customer: IG1's own people. That list is small,\n    and it is the honest answer to \"who is in MY organisation\".\n  * ``project_id=<id>`` — one named account. The drill-down the Tenants\n    page opens.\n  * ``scope=all`` — every account, each row carrying ``project_id`` and\n    ``project_name``. Platform admin only, and it must be asked for: an\n    endpoint that spans every tenant by accident is how the flat list\n    happened in the first place.\n\nEvery row now carries its owning tenant in both readings, so a member\nlist can never again be shown without saying whose members they are.","operationId":"list_org_users_v1_org_users_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"List the members of ONE named tenant. A platform admin may name any tenant; everyone else may only name their own (any other value answers 404, so a tenant id is never confirmable from outside).","title":"Project Id"},"description":"List the members of ONE named tenant. A platform admin may name any tenant; everyone else may only name their own (any other value answers 404, so a tenant id is never confirmable from outside)."},{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(tenant|all)$"},{"type":"null"}],"description":"'tenant' (the default) lists ONE account's members. 'all' lists every tenant's members with the owning tenant on each row, and is platform-admin only — it is the Tenants page's view, not the Security page's.","title":"Scope"},"description":"'tenant' (the default) lists ONE account's members. 'all' lists every tenant's members with the owning tenant on each row, and is platform-admin only — it is the Tenants page's view, not the Security page's."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":8,"users":[{"billing_access":true,"display_name":"Example User","email":"user-01@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_ACTIVE","user_id":"000000000000000006"},{"billing_access":false,"display_name":"Example User","email":"user-03@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_ACTIVE","user_id":"000000000000000004"},{"billing_access":false,"display_name":"Example User","email":"user-05@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_INITIAL","user_id":"000000000000000009"},{"billing_access":false,"display_name":"Example User","email":"user-07@example.com","roles":["admin"],"state":"USER_STATE_ACTIVE","user_id":"000000000000000003"},{"billing_access":false,"display_name":"Example User","email":"user-08@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_INITIAL","user_id":"000000000000000008"},{"billing_access":false,"display_name":"Example User","email":"user-10@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_INITIAL","user_id":"000000000000000010"},{"billing_access":false,"display_name":"Example User","email":"user-11@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_INITIAL","user_id":"000000000000000005"},{"billing_access":false,"display_name":"Example User","email":"user-12@example.com","roles":["customer","customer-owner"],"state":"USER_STATE_INITIAL","user_id":"000000000000000007"}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/org/users/invite":{"post":{"tags":["credentials"],"summary":"Invite Org User","description":"Add a human to ONE account: create their login if they have none,\nattach them if they already do, and grant the requested PoC role. A\ncustomer-admin can grant ONLY customer/customer-viewer/customer-engineer/\ncustomer-admin (the request schema itself caps the role — admin never\nappears).\n\nATTACHING AN EXISTING PERSON (2026-09-22). This used to call\n``POST /users/human`` unconditionally, so an address that already had an\nIG1 Cloud login came back as Zitadel's own 409 — \"User already exists\n(V3-DKcYh)\" — wrapped in a 502 and shown to the customer verbatim. In a\ndeployment where EVERY tenant shares one Zitadel org, that is not the\nedge case; it is the ordinary one. It also made a remedy the platform\nalready prints impossible to follow: the signup refusal for a duplicate\norganisation tells the applicant to \"ask the owner or an administrator to\ninvite this person\", and invite was the one thing that could not do it.\n\nThe three outcomes, by who owns the existing login:\n\n  * nobody owns it (or it does not exist) — created if needed, granted,\n    bound to the target account. 201.\n  * the TARGET account already owns it — the person is already a member;\n    their role is brought to what was asked and the call answers 200. An\n    invite that is a no-op must say 200, not invent a second membership.\n  * ANOTHER account owns it — 409 naming the remedy. A customer-admin\n    must not be able to pull a stranger's identity into their own tenant\n    on the strength of guessing an address, and the refusal must not\n    confirm which account holds it either. A platform admin, who\n    legitimately spans every tenant, may attach across the boundary by\n    naming the target explicitly: that ADDS an entitlement (phase 47),\n    it never moves the person out of the account they already have.\n\nW9/B follow-up: the binding written here is what makes the invitee a\nmember — without it resolve_tenant finds nothing, and require_auth\nanswers 403 \"No tenant assigned\".","operationId":"invite_org_user_v1_org_users_invite_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInvite"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/org/users/{user_id}/billing":{"put":{"tags":["credentials"],"summary":"Set Member Billing","description":"Grant or revoke billing visibility for one member (2026-08-13).\n\nOWNER-ONLY, deliberately narrower than the other member-management\nsurfaces: cost is competitive information inside a company, and AWS\nmodels the equivalent switch (\"Activate IAM access to Billing\") as\nroot-only. A platform admin also passes, because support has to be able\nto fix a locked-out account.\n\nThe flag lands on the member's credential-store entry, so it takes\neffect on their next request (the resolution cache is <=60 s) — no\nre-login, no restart. Cross-tenant user ids answer 404, never 403,\nthe same non-confirmability rule every member surface follows.","operationId":"set_member_billing","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingGrant"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/org/users/{user_id}":{"delete":{"tags":["credentials"],"summary":"Remove Org User","description":"Remove an org user OF THE CALLER'S TENANT.\n\nTwo guards, both W9/B (2026-08-08 audit §2.4):\n  * ownership — a user that does not resolve to the caller's own project\n    answers 404, never 403: cross-tenant user ids must not be\n    confirmable (the same rule revoke_credential follows);\n  * privilege escalation — a customer-admin cannot remove a user holding\n    the platform admin role, even inside their own tenant.\nA platform admin keeps the org-wide removal path.\n\nAn invite creates two things — the person's login, and the membership\nrecord binding them to this tenant — and removal undoes both. The\nmembership record counts against the account's credential allowance while\nit lives, so the slot is released here: `credentials_revoked` in the\nresponse names the record ids that were closed.","operationId":"remove_org_user_v1_org_users__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/object-storage/buckets":{"get":{"tags":["object-storage"],"summary":"List Buckets","description":"List the tenant's buckets with size/object stats (tier 0+).\n\n409 for a platform credential rather than the empty list it used to\nreturn: ``{\"count\": 0}`` is a claim about a namespace, and answering it\nfor an identity that HAS no namespace tells an operator their tenant\nlost its buckets.","operationId":"list_object_storage_buckets","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"buckets":[{"modified":"2026-08-22T22:22:00.535399Z","name":"compat45-1787437283","objects":0,"size_bytes":0}],"count":1,"endpoint":"http://10.57.8.76:7480"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["object-storage"],"summary":"Create Bucket","description":"Create a bucket under the tenant's RGW user (tier 1+). The user is\nensured first (created with a generated S3 keypair on first call).\n\nQUOTA ADMISSION (audit gap B3, 2026-08-14): the tier's object ceilings\nwere advertised on /v1/quotas as enforced while nothing refused anything.\nBucket create is the admission point, and it does three things in order:\n\n  1. resolve the tenant's tier — fail closed (503) when unreadable;\n  2. re-stamp the tier's RGW user quota (idempotent absolute write).\n     The tier-change hook (tenants.apply_tier) stamps the RGW side\n     itself since the B3 follow-up, so this re-stamp is the BELT for\n     the cases the hook cannot reach: pre-B3 users that predate quota\n     stamping, and a tier change that skipped RGW because the tenant's\n     user did not exist yet;\n  3. count the tenant's buckets against object.max_buckets and refuse\n     with the numbers — RGW's own TooManyBuckets would fire eventually,\n     but only for users created after this change, and its refusal names\n     no tier and no way out.","operationId":"create_object_storage_bucket","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BucketCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"buckets":[{"modified":"2026-08-22T22:22:00.535399Z","name":"compat45-1787437283","objects":0,"size_bytes":0}],"count":1,"endpoint":"http://10.57.8.76:7480"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/object-storage/buckets/{name}":{"delete":{"tags":["object-storage"],"summary":"Delete Bucket","description":"Delete a bucket (tier 2). RGW refuses a non-empty bucket (409) and an\nunknown one (404 NoSuchBucket) — both surface verbatim.","operationId":"delete_object_storage_bucket","security":[{"HTTPBearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/object-storage/buckets/{name}/objects":{"get":{"tags":["object-storage"],"summary":"List Objects","description":"One page of the bucket's objects (tier 0+).\n\nSigned with the TENANT's own RGW keypair, whose tenanted uid gives it a\nprivate namespace: another tenant's bucket name does not resolve for\nthis credential, and RGW's own NoSuchBucket surfaces verbatim. There is\nno server-side \"is this bucket yours\" check to keep in sync — the\ncredential IS the scope.\n\nPaging is S3's: the response's ``next_continuation_token`` is fed back\nas ``continuation_token``. Object BYTES are not served here (see the\npresigned route) — this pod must never buffer an object.","operationId":"list_object_storage_objects","security":[{"HTTPBearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"prefix","in":"query","required":false,"schema":{"type":"string","description":"List only keys starting with this prefix","default":"","title":"Prefix"},"description":"List only keys starting with this prefix"},{"name":"continuation_token","in":"query","required":false,"schema":{"type":"string","description":"Opaque cursor returned as next_continuation_token by the previous page","default":"","title":"Continuation Token"},"description":"Opaque cursor returned as next_continuation_token by the previous page"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/object-storage/buckets/{name}/objects/{key}/presigned":{"get":{"tags":["object-storage"],"summary":"Presign Object","description":"A short-lived presigned GET URL for one object (tier 0+).\n\nWhy a URL instead of the bytes: the gateway buffers bodies in RAM, so\nproxying a multi-GB object would OOM this pod and break the API for\nevery tenant to serve one download. The customer's browser fetches the\nobject straight from RGW; the bytes never enter this process.\n\nThe URL is a bearer capability for that one object, so it is capped at\n15 minutes and the response STATES both the granted lifetime and the\nabsolute instant it dies — the holder must be able to see what it holds.\nIt is signed with the tenant's own keypair: it can never reach outside\nthat tenant's RGW namespace, whatever bucket name is asked for.","operationId":"presign_object_storage_object","security":[{"HTTPBearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"expires_in","in":"query","required":false,"schema":{"type":"integer","maximum":900,"minimum":30,"description":"Link lifetime in seconds (30 to 900); the response states what was granted","default":300,"title":"Expires In"},"description":"Link lifetime in seconds (30 to 900); the response states what was granted"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/object-storage/buckets/{name}/versioning":{"get":{"tags":["object-storage"],"summary":"Get Bucket Versioning","description":"The bucket's versioning state (tier 0+): enabled / suspended / unset.\n\nSigned with the TENANT's own RGW keypair, so another tenant's bucket\nname is refused by RGW itself and its error surfaces verbatim.","operationId":"get_object_storage_bucket_versioning","security":[{"HTTPBearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["object-storage"],"summary":"Set Bucket Versioning","description":"Enable or suspend versioning on the bucket (tier 1+).\n\nTier 1, not 2: enabling versioning only ever ADDS protection, and\nsuspending it destroys nothing (existing versions are kept — S3 has no\ntransition that deletes them). The tier-2 class is for calls that lose\ndata.","operationId":"set_object_storage_bucket_versioning","security":[{"HTTPBearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersioningUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/object-storage/credentials":{"get":{"tags":["object-storage"],"summary":"Get Credentials","description":"The tenant's S3 keypair (tier 0+). Creates the RGW user on first\ncall (the lazy model). The secret is returned on EVERY call — it is the\ncaller's own key, re-returned by RGW; it is never persisted anywhere\nnew.\n\nThis is the doorway to DIRECT S3 access, so the RGW user must never be\ncreated here without its tier quota (audit gap B3): the tier is resolved\nfail-closed and rides into ensure_tenant_user, which stamps the RGW\nceilings the moment the user exists.","operationId":"get_object_storage_credentials","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"access_key":"tenant$<project_id>-EXAMPLEKEY0000000","endpoint":"http://10.57.8.76:7480","secret_key":"<returned once — store it now>","uid":"tenant$00000000000000000000000000000001"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/object-storage/credentials/rotate":{"post":{"tags":["object-storage"],"summary":"Rotate Credentials","description":"Mint a NEW S3 keypair for the tenant (tier 1+); returns the new\npair. Old keys stay active — revoke is an operator action.\n\nRotate can CREATE the RGW user (the lazy 404 path), so the tier quota\nrides along exactly as it does on GET /credentials — no path that hands\nout S3 keys may mint an unquota'd user (audit gap B3).","operationId":"rotate_object_storage_credentials","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/databases":{"get":{"tags":["databases"],"summary":"List Databases","description":"List the tenant's database instances (tier 0+), CNPG Clusters and\nStrimzi Kafkas in the tenant namespace mapped to the uniform shape.","operationId":"list_databases","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"databases":[]}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["databases"],"summary":"Create Database","description":"Provision an instance (tier 1+): ensure the tenant namespace +\nResourceQuota, then create the CNPG Cluster (postgres) or the Strimzi\nKafka + KafkaNodePool (kafka). The shape returned is the uniform one —\nstatus starts at ``pending`` until the operator reports.","operationId":"create_database","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"databases":[]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/databases/{engine}/{name}/restore":{"post":{"tags":["databases"],"summary":"Restore Database","description":"Restore a backup-enabled instance into a NEW cluster (tier 1).\n\nNever in place, by the same rule as the identity cutover: the source\nstays live and untouched, and the recovered cluster is a new live\ninstance with its own archive identity. A source without backup\nenabled answers 422 naming the phase that turns it on; a kafka\ninstance answers 422 because there is no archive to restore from.\nThe budget gate is the create gate — the tenant pays for the new\ninstance like any other.","operationId":"restore_database","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseRestore"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/databases/{engine}/{name}":{"get":{"tags":["databases"],"summary":"Get Database","description":"Instance detail (tier 0+): the uniform shape + ready flag + the\nin-cluster endpoint + an events digest.","operationId":"get_database","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["databases"],"summary":"Resize Database","description":"Grow an instance's storage and/or scale its replica count (tier 1+).\n\nStorage is GROW-ONLY, and a shrink is a 409 rather than a best-effort\nattempt: neither CNPG nor a PersistentVolumeClaim shrinks in place, so\nmaking an instance smaller means recreating the volume and restoring the\ndata — a migration with a cutover, not a resize. Asking for the current\nsize is refused too; answering 200 to a no-op teaches the caller something\nfalse.\n\n``replicas`` (PostgreSQL) changes IN PLACE in either direction — CNPG\nbootstraps or retires an instance live, and a down-scale loses no data\nbecause every synchronous replica holds every byte. Asking for the current\ncount is refused for the same reason a no-op size is.\n\nA request above the tenant's storage budget is refused BEFORE anything is\npatched, with the arithmetic that refused it. The CR patch and the PVC\nexpansion are separately admitted, so a resize allowed here and refused by\nthe namespace ResourceQuota later would leave the instance advertising a\nsize it never gets.\n\nBoth operators expand the volume through the PVC, which needs the storage\nclass to allow volume expansion. When it does not, the operator's own error\nreaches the caller unchanged — via this call if an admission webhook\nrejects it, otherwise in the events digest on GET /v1/databases/{engine}/\n{name}. It is never restated in our words.","operationId":"resize_database","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseResize"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["databases"],"summary":"Delete Database","description":"Delete the instance's CR (tier 2; Kafka deletes its node pool too).\nThe tenant namespace is left behind empty — namespaces are never\ndeleted by the API.","operationId":"delete_database","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/databases/{engine}/{name}/credentials":{"get":{"tags":["databases"],"summary":"Get Database Credentials","description":"The instance's connection credentials (tier 0+).\n\nTier 0 because these are the caller's OWN credentials to their OWN\ndatabase — the same reasoning that makes GET /v1/object-storage/\ncredentials tier 0. Postgres returns the CNPG application user\n(username/password/uri/host/port/dbname); Kafka returns the bootstrap\naddress with ``credentials: null``, because the rendered listener is\nplaintext in-cluster and has no credential to hand over.\n\nThe namespace is derived from the resolved tenant and CANNOT be\ninfluenced by the caller (databases.get_connection_credentials takes no\nnamespace argument at all). A still-provisioning cluster whose Secret\ndoes not exist yet answers 404 \"credentials not ready\", which is a\ndifferent fact from \"no such database\".","operationId":"get_database_credentials","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/databases/{engine}/{name}/rotate-credentials":{"post":{"tags":["databases"],"summary":"Rotate Database Credentials","description":"Rotate the PostgreSQL application user's password (tier 1+).\n\nTHE NEW PASSWORD IS NOT IN THIS RESPONSE. Re-read it from\nGET /v1/databases/{engine}/{name}/credentials, which already exists and\nalready goes through the ig1-secrets broker. Returning it here as well would\nput a live credential in a second place — this body, a shell history, an\naccess log, an agent transcript, every retry of a lost 200 — for no\ncapability the caller does not already have.\n\nDISRUPTIVE, and the response says so. PostgreSQL checks the password when a\nconnection is opened, not per statement: open connections survive the\nrotation and every application still holding the old value fails the moment\nit reconnects. That is what makes rotating \"just to be safe\" an outage.\n\nKafka answers 404: the rendered listener is plaintext in-cluster with no\nKafkaUser, so there is no credential to rotate and inventing one would\nreport success for something that did not happen.","operationId":"rotate_database_credentials","security":[{"HTTPBearer":[]}],"parameters":[{"name":"engine","in":"path","required":true,"schema":{"type":"string","title":"Engine"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/nat-gateways":{"get":{"tags":["nat-gateways"],"summary":"List Nat Gateways","description":"The tenant's NAT gateway (tier 0). Empty list when the network has\nnot been provisioned yet.","operationId":"list_nat_gateways","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["nat-gateways"],"summary":"Create Nat Gateway","description":"Converge SNAT on the tenant's router (tier 1).\n\nIdempotent: a tenant with an active gateway gets their existing record\nback. A tenant without a router gets 404 — the network substrate must be\nprovisioned first (tenants.ensure_tenant_network).\n\nQuota refusal states the tier's ceiling and what to do next.","operationId":"create_nat_gateway","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/nat-gateways/{ngw_id}":{"get":{"tags":["nat-gateways"],"summary":"Get Nat Gateway","description":"One NAT gateway (tier 0). Another tenant's id answers 404.","operationId":"get_nat_gateway","security":[{"HTTPBearer":[]}],"parameters":[{"name":"ngw_id","in":"path","required":true,"schema":{"type":"string","title":"Ngw Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["nat-gateways"],"summary":"Delete Nat Gateway","description":"Disable SNAT on the tenant's router (tier 2).\n\nThe router's external gateway network SURVIVES so floating IPs continue\nto work. Re-creating re-enables SNAT.","operationId":"delete_nat_gateway","security":[{"HTTPBearer":[]}],"parameters":[{"name":"ngw_id","in":"path","required":true,"schema":{"type":"string","title":"Ngw Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workload-identity/token-exchange":{"post":{"tags":["workload-identity"],"summary":"Token Exchange","description":"Exchange a k8s service-account JWT for an IG1 credential.\n\nFlow:\n  1. Verify the subject_token JWT signature against the cluster's OIDC\n     discovery endpoint.\n  2. Extract ``iss`` (issuer) and ``sub`` (service account name).\n  3. Resolve the tenant project from the issuer → project mapping.\n  4. Enforce tenancy: platform admins may exchange for any tenant;\n     customer callers may exchange only for their own tenant.\n  5. Mint a short-lived IG1 access token scoped to that tenant.\n  6. Return the token (Bearer, 1 h TTL).","operationId":"exchangeWorkloadIdentityToken","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenExchangeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenExchangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/status":{"get":{"tags":["status"],"summary":"Get Status","description":"The customer status document (tier 0+): derived per-service health\nand the open incidents/maintenance list.\n\nCarries NO infrastructure: no node counts, no alarm counts, and no probe\ntext naming the components behind a service (ig1_api/status.py says why).\nOn the operator plane each row additionally carries `probe`, the probe's\nown words — the diagnostic that used to be published to tenants.","operationId":"get_status","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"generated_at":"2026-08-23T04:26:06Z","incidents":[],"maintenance":[],"overall":"ok","services":[{"detail":"Operating normally","service":"compute","status":"ok"},{"detail":"Operating normally","service":"storage","status":"ok"},{"detail":"Operating normally","service":"network","status":"ok"},{"detail":"Operating normally","service":"portal","status":"ok"},{"detail":"Operating normally","service":"api","status":"ok"},{"detail":"Operating normally","service":"billing","status":"ok"},{"detail":"Operating normally","service":"events","status":"ok"},{"detail":"Operating normally","service":"object-storage","status":"ok"},{"detail":"Operating normally","service":"databases","status":"ok"}]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/status/incidents":{"post":{"tags":["status"],"summary":"Create Incident","description":"Declare an incident or maintenance notice (tier 2 + platform-admin).\nSeeded empty by default — the page supports planned maintenance\nannouncements from day one.","operationId":"create_status_incident","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/IncidentCreate"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/status/incidents/{incident_id}":{"delete":{"tags":["status"],"summary":"Delete Incident","description":"Remove an incident/maintenance notice (tier 2 + platform-admin).","operationId":"delete_status_incident","security":[{"HTTPBearer":[]}],"parameters":[{"name":"incident_id","in":"path","required":true,"schema":{"type":"string","title":"Incident Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/cost/breakdown":{"get":{"tags":["w7-proxy"],"summary":"Proxy Cost Breakdown","description":"Rated-usage aggregation (tier 0+), proxied to the billing service.","operationId":"proxy_cost_breakdown","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":1,"currency":"eur","group_by":"service","period":{"end":"2026-08-23T04:25:55.439617+00:00","start":"2026-08-01T00:00:00+00:00"},"project_id":"00000000000000000000000000000001","rows":[{"amount_cents":61,"amount_eur":"0.61","currency":"eur","quantity":122.455122,"service":"network","unit":"fip-hour"}],"tenant_name":"ig1-customer-example-1","total_cents":61,"total_eur":"0.61"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/cost/forecast":{"get":{"tags":["w7-proxy"],"summary":"Proxy Cost Forecast","description":"The month run-rate forecast (tier 0+), proxied to billing.","operationId":"proxy_cost_forecast","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"currency":"eur","elapsed_seconds":1916756,"forecast_cents":85,"forecast_eur":"0.85","method":"linear-run-rate","method_detail":"month-to-date rated spend x (seconds in month / seconds elapsed in month); a straight-line projection of the current run rate — no ML, no seasonality","month_to_date_cents":61,"month_to_date_eur":"0.61","period":{"end":"2026-08-23T04:25:56.186276+00:00","start":"2026-08-01T00:00:00+00:00"},"period_seconds":2678400,"project_id":"00000000000000000000000000000001","tenant_name":"ig1-customer-example-1"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/budgets":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Budgets","description":"The tenant's budgets (tier 0+), proxied to billing.","operationId":"proxy_list_budgets","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"budgets":[{"amount_cents":424200,"budget_id":"bdg-48e57c6deab5","created_at":"2026-08-21T16:07:35.496056Z","created_by":"000000000000000011","currency":"EUR","last_breached_period":null,"period":"monthly","project_id":"00000000000000000000000000000001","tenant_name":"ig1-customer-example-1"}],"count":1}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["w7-proxy"],"summary":"Proxy Create Budget","description":"Create a budget (tier 1+), proxied to billing.","operationId":"proxy_create_budget","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"budgets":[{"amount_cents":424200,"budget_id":"bdg-48e57c6deab5","created_at":"2026-08-21T16:07:35.496056Z","created_by":"000000000000000011","currency":"EUR","last_breached_period":null,"period":"monthly","project_id":"00000000000000000000000000000001","tenant_name":"ig1-customer-example-1"}],"count":1}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/budgets/{budget_id}":{"delete":{"tags":["w7-proxy"],"summary":"Proxy Delete Budget","description":"Delete a budget (tier 2), proxied to billing.","operationId":"proxy_delete_budget","security":[{"HTTPBearer":[]}],"parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string","title":"Budget Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/invoices":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Invoices","description":"The existing invoices list (tier 0+), proxied to billing.","operationId":"proxy_list_invoices","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":47,"invoices":[{"created_at":"2026-08-17T16:00:54.347414Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-17T16:00:54.347414Z"},{"detail":"stripe invoice in_1U5Sy3EPBjrbSOyndP93HwFd","event":"stripe_attached","ts":"2026-08-17T16:00:55.566530Z"}],"invoice_id":"in-bf2c5689f2b1","lines":[],"notify_events":[],"period_end":"2026-08-17T16:00:53Z","period_start":"2026-08-17T15:00:53Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5Sy3EPBjrbSOyndP93HwFd","tenant_name":"00000000000000000000000000000001","total_cents":0},{"created_at":"2026-08-17T17:12:39.915831Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-17T17:12:39.915831Z"},{"detail":"stripe invoice in_1U5U5UEPBjrbSOynUJLaESK5","event":"stripe_attached","ts":"2026-08-17T17:12:40.750219Z"}],"invoice_id":"in-ff69988634eb","lines":[],"notify_events":[],"period_end":"2026-08-17T17:12:39Z","period_start":"2026-08-17T16:12:39Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5U5UEPBjrbSOynUJLaESK5","tenant_name":"00000000000000000000000000000001","total_cents":0},{"created_at":"2026-08-17T18:14:19.856045Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-17T18:14:19.856045Z"},{"detail":"stripe invoice in_1U5V3AEPBjrbSOynjJWPlKK1","event":"stripe_attached","ts":"2026-08-17T18:14:21.364280Z"}],"invoice_id":"in-1392f9b5594e","lines":[{"amount_cents":0,"amount_eur":"0.001865280","description":"Floating IPs — 1 address(es), 0.37 hours @ 0.005 EUR/h","key":"fip","quantity":0.373056,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-17T18:14:19Z","period_start":"2026-08-17T17:14:19Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5V3AEPBjrbSOynjJWPlKK1","tenant_name":"00000000000000000000000000000001","total_cents":0},{"created_at":"2026-08-18T02:52:09.415627Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-18T02:52:09.415627Z"},{"detail":"stripe invoice in_1U5d8HEPBjrbSOynyENfU2GJ","event":"stripe_attached","ts":"2026-08-18T02:52:10.791279Z"}],"invoice_id":"in-e7848d87015a","lines":[{"amount_cents":0,"amount_eur":"0.004456945","description":"Floating IPs — 1 address(es), 0.89 hours @ 0.005 EUR/h","key":"fip","quantity":0.891389,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-18T02:52:06Z","period_start":"2026-08-18T01:52:06Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5d8HEPBjrbSOynyENfU2GJ","tenant_name":"00000000000000000000000000000001","total_cents":0},{"created_at":"2026-08-18T03:59:02.160264Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-18T03:59:02.160264Z"},{"detail":"stripe invoice in_1U5eB0EPBjrbSOynGEf5Qcaj","event":"stripe_attached","ts":"2026-08-18T03:59:03.295020Z"}],"invoice_id":"in-d8afaf3fc688","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-18T03:59:01Z","period_start":"2026-08-18T02:59:01Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5eB0EPBjrbSOynGEf5Qcaj","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-18T06:58:41.842566Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-18T06:58:41.842566Z"},{"detail":"stripe invoice in_1U5gysEPBjrbSOynYhLPUTXb","event":"stripe_attached","ts":"2026-08-18T06:58:43.007266Z"}],"invoice_id":"in-c0c53d247f66","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-18T06:58:41Z","period_start":"2026-08-18T05:58:41Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5gysEPBjrbSOynYhLPUTXb","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-18T07:39:48.446502Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-18T07:39:48.446502Z"},{"detail":"stripe invoice in_1U5hceEPBjrbSOynD4b1L4Bm","event":"stripe_attached","ts":"2026-08-18T07:39:49.652262Z"}],"invoice_id":"in-21ee4103d7eb","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-18T07:39:47Z","period_start":"2026-08-18T06:39:47Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5hceEPBjrbSOynD4b1L4Bm","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-18T08:05:31.996485Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-18T08:05:31.996485Z"},{"detail":"stripe invoice in_1U5i1YEPBjrbSOynHPijfnSw","event":"stripe_attached","ts":"2026-08-18T08:05:33.296363Z"}],"invoice_id":"in-f5626569a1a7","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-18T08:05:31Z","period_start":"2026-08-18T07:05:31Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U5i1YEPBjrbSOynHPijfnSw","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:13:30.713135Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:13:30.713135Z"},{"detail":"stripe invoice in_1U6C7LEPBjrbSOynAn6h0HsC","event":"stripe_attached","ts":"2026-08-19T16:13:31.994772Z"}],"invoice_id":"in-4ab149539900","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:13:29Z","period_start":"2026-08-19T15:13:29Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6C7LEPBjrbSOynAn6h0HsC","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:30:23.488646Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:30:23.488646Z"},{"detail":"stripe invoice in_1U6CNfEPBjrbSOynoWLu3a8C","event":"stripe_attached","ts":"2026-08-19T16:30:24.688660Z"}],"invoice_id":"in-f30a5ef4ee0c","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:30:22Z","period_start":"2026-08-19T15:30:22Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6CNfEPBjrbSOynoWLu3a8C","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:33:38.472336Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:33:38.472336Z"},{"detail":"stripe invoice in_1U6CQoEPBjrbSOynu7gLAxE3","event":"stripe_attached","ts":"2026-08-19T16:33:39.644776Z"}],"invoice_id":"in-359b09005bd8","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:33:37Z","period_start":"2026-08-19T15:33:37Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6CQoEPBjrbSOynu7gLAxE3","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:35:13.030840Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:35:13.030840Z"},{"detail":"stripe invoice in_1U6CSLEPBjrbSOynHLUXx8UL","event":"stripe_attached","ts":"2026-08-19T16:35:14.245941Z"}],"invoice_id":"in-58510744f8f2","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:35:11Z","period_start":"2026-08-19T15:35:11Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6CSLEPBjrbSOynHLUXx8UL","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:37:29.432461Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:37:29.432461Z"},{"detail":"stripe invoice in_1U6CUXEPBjrbSOynyGNpdEKk","event":"stripe_attached","ts":"2026-08-19T16:37:30.583093Z"}],"invoice_id":"in-e5d2affe7aa0","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:37:28Z","period_start":"2026-08-19T15:37:28Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6CUXEPBjrbSOynyGNpdEKk","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T16:55:52.531243Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T16:55:52.531243Z"},{"detail":"stripe invoice in_1U6CmLEPBjrbSOynJgwvGuI5","event":"stripe_attached","ts":"2026-08-19T16:55:53.869675Z"}],"invoice_id":"in-7aa341b32fc7","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T16:55:51Z","period_start":"2026-08-19T15:55:51Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6CmLEPBjrbSOynJgwvGuI5","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T17:10:22.820789Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T17:10:22.820789Z"},{"detail":"stripe invoice in_1U6D0NEPBjrbSOyn13pzcpgX","event":"stripe_attached","ts":"2026-08-19T17:10:23.971866Z"}],"invoice_id":"in-7d8c44dd41e5","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T17:10:21Z","period_start":"2026-08-19T16:10:21Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6D0NEPBjrbSOyn13pzcpgX","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-19T17:27:00.467615Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-19T17:27:00.467615Z"},{"detail":"stripe invoice in_1U6DGTEPBjrbSOyndbqyQhtL","event":"stripe_attached","ts":"2026-08-19T17:27:01.868253Z"}],"invoice_id":"in-0c32bafff882","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-19T17:26:59Z","period_start":"2026-08-19T16:26:59Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6DGTEPBjrbSOyndbqyQhtL","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T16:58:08.680909Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T16:58:08.680909Z"},{"detail":"stripe invoice in_1U6ZI5EPBjrbSOynx04RoGpA","event":"stripe_attached","ts":"2026-08-20T16:58:10.138104Z"}],"invoice_id":"in-bfe6073cd62a","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T16:58:07Z","period_start":"2026-08-20T15:58:07Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6ZI5EPBjrbSOynx04RoGpA","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T17:45:28.689385Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T17:45:28.689385Z"},{"detail":"stripe invoice in_1U6a1tEPBjrbSOynQ1xFvL8X","event":"stripe_attached","ts":"2026-08-20T17:45:29.939710Z"}],"invoice_id":"in-9dfdb252cf70","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T17:45:27Z","period_start":"2026-08-20T16:45:27Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6a1tEPBjrbSOynQ1xFvL8X","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T20:04:03.154345Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T20:04:03.154345Z"},{"detail":"stripe invoice in_1U6cBzEPBjrbSOyni3PGPEnw","event":"stripe_attached","ts":"2026-08-20T20:04:04.291173Z"}],"invoice_id":"in-4e7a72dfad6d","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T20:04:01Z","period_start":"2026-08-20T19:04:01Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6cBzEPBjrbSOyni3PGPEnw","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T20:17:59.305851Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T20:17:59.305851Z"},{"detail":"stripe invoice in_1U6cPTEPBjrbSOynKNpWK5TH","event":"stripe_attached","ts":"2026-08-20T20:18:00.449926Z"}],"invoice_id":"in-ace354d0e69a","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T20:17:58Z","period_start":"2026-08-20T19:17:58Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6cPTEPBjrbSOynKNpWK5TH","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T20:18:12.816925Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T20:18:12.816925Z"},{"detail":"stripe invoice in_1U6cPhEPBjrbSOynyYQY1wq3","event":"stripe_attached","ts":"2026-08-20T20:18:13.943273Z"}],"invoice_id":"in-99859c32472c","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T20:18:11Z","period_start":"2026-08-20T19:18:11Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6cPhEPBjrbSOynyYQY1wq3","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T21:09:52.969356Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T21:09:52.969356Z"},{"detail":"stripe invoice in_1U6dDhEPBjrbSOyn6cWSLOEc","event":"stripe_attached","ts":"2026-08-20T21:09:54.126657Z"}],"invoice_id":"in-8901707749ee","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T21:09:50Z","period_start":"2026-08-20T20:09:50Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6dDhEPBjrbSOyn6cWSLOEc","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T22:19:11.450316Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T22:19:11.450316Z"},{"detail":"stripe invoice in_1U6eIlEPBjrbSOynw6D5OZX1","event":"stripe_attached","ts":"2026-08-20T22:19:12.597929Z"}],"invoice_id":"in-c465a745c3f1","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T22:19:10Z","period_start":"2026-08-20T21:19:10Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6eIlEPBjrbSOynw6D5OZX1","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-20T23:40:48.959805Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-20T23:40:48.959805Z"},{"detail":"stripe invoice in_1U6fZlEPBjrbSOyn7z62o4nM","event":"stripe_attached","ts":"2026-08-20T23:40:50.176878Z"}],"invoice_id":"in-e853ff91f42d","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-20T23:40:47Z","period_start":"2026-08-20T22:40:47Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6fZlEPBjrbSOyn7z62o4nM","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T11:39:50.886772Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T11:39:50.886772Z"},{"detail":"stripe invoice in_1U6qnbEPBjrbSOynkNJl1khA","event":"stripe_attached","ts":"2026-08-21T11:39:52.082459Z"}],"invoice_id":"in-759f0f506373","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T11:39:49Z","period_start":"2026-08-21T10:39:49Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6qnbEPBjrbSOynkNJl1khA","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T11:52:26.343432Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T11:52:26.343432Z"},{"detail":"stripe invoice in_1U6qzmEPBjrbSOynEW0ysQwH","event":"stripe_attached","ts":"2026-08-21T11:52:27.449055Z"}],"invoice_id":"in-26e0c5d84ef4","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T11:52:25Z","period_start":"2026-08-21T10:52:25Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6qzmEPBjrbSOynEW0ysQwH","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:10:12.392103Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:10:12.392103Z"},{"detail":"stripe invoice in_1U6rGyEPBjrbSOyneNJLwiju","event":"stripe_attached","ts":"2026-08-21T12:10:13.575251Z"}],"invoice_id":"in-5c6dd3ebb5e2","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:10:11Z","period_start":"2026-08-21T11:10:11Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rGyEPBjrbSOyneNJLwiju","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:10:39.985759Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:10:39.985759Z"},{"detail":"stripe invoice in_1U6rHQEPBjrbSOynEhVfBtCt","event":"stripe_attached","ts":"2026-08-21T12:10:41.116547Z"}],"invoice_id":"in-4e03a24d4ce6","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:10:39Z","period_start":"2026-08-21T11:10:39Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rHQEPBjrbSOynEhVfBtCt","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:29:52.635857Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:29:52.635857Z"},{"detail":"stripe invoice in_1U6ra1EPBjrbSOynXfpEq1PH","event":"stripe_attached","ts":"2026-08-21T12:29:53.823201Z"}],"invoice_id":"in-36cd1664856b","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:29:52Z","period_start":"2026-08-21T11:29:52Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6ra1EPBjrbSOynXfpEq1PH","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:34:34.198232Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:34:34.198232Z"},{"detail":"stripe invoice in_1U6reYEPBjrbSOynus6NOKE1","event":"stripe_attached","ts":"2026-08-21T12:34:35.536342Z"}],"invoice_id":"in-e778d2e8a711","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:34:33Z","period_start":"2026-08-21T11:34:33Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6reYEPBjrbSOynus6NOKE1","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:35:06.678320Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:35:06.678320Z"},{"detail":"stripe invoice in_1U6rf5EPBjrbSOynvDtvOLcD","event":"stripe_attached","ts":"2026-08-21T12:35:07.814099Z"}],"invoice_id":"in-106477245165","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:35:06Z","period_start":"2026-08-21T11:35:06Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rf5EPBjrbSOynvDtvOLcD","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:37:00.562417Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:37:00.562417Z"},{"detail":"stripe invoice in_1U6rgvEPBjrbSOynyOdf8JgY","event":"stripe_attached","ts":"2026-08-21T12:37:01.666428Z"}],"invoice_id":"in-6edfd9414a57","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:37:00Z","period_start":"2026-08-21T11:37:00Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rgvEPBjrbSOynyOdf8JgY","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:37:42.821042Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:37:42.821042Z"},{"detail":"stripe invoice in_1U6rhbEPBjrbSOynyy2aTQd8","event":"stripe_attached","ts":"2026-08-21T12:37:43.996481Z"}],"invoice_id":"in-93ccd0416fb0","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:37:42Z","period_start":"2026-08-21T11:37:42Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rhbEPBjrbSOynyy2aTQd8","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T12:39:30.054606Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T12:39:30.054606Z"},{"detail":"stripe invoice in_1U6rjKEPBjrbSOynkJWzkGbB","event":"stripe_attached","ts":"2026-08-21T12:39:31.183420Z"}],"invoice_id":"in-4fe96a9dd36c","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T12:39:29Z","period_start":"2026-08-21T11:39:29Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6rjKEPBjrbSOynkJWzkGbB","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-21T19:20:00.903514Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-21T19:20:00.903514Z"},{"detail":"stripe invoice in_1U6xyvEPBjrbSOynSBYnU3jC","event":"stripe_attached","ts":"2026-08-21T19:20:02.245366Z"}],"invoice_id":"in-68654e14454b","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-21T19:19:59Z","period_start":"2026-08-21T18:19:59Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U6xyvEPBjrbSOynSBYnU3jC","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T16:39:20.986610Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T16:39:20.986610Z"},{"detail":"stripe invoice in_1U7HwzEPBjrbSOynZyzEr57U","event":"stripe_attached","ts":"2026-08-22T16:39:22.217639Z"}],"invoice_id":"in-cc52c2639eaf","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T16:39:19Z","period_start":"2026-08-22T15:39:19Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7HwzEPBjrbSOynZyzEr57U","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T16:56:33.255466Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T16:56:33.255466Z"},{"detail":"stripe invoice in_1U7IDdEPBjrbSOynWoZV93Ro","event":"stripe_attached","ts":"2026-08-22T16:56:34.407631Z"}],"invoice_id":"in-3c0e15488d30","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T16:56:31Z","period_start":"2026-08-22T15:56:31Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7IDdEPBjrbSOynWoZV93Ro","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T17:24:24.330043Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T17:24:24.330043Z"},{"detail":"stripe invoice in_1U7IeaEPBjrbSOynxT7L7HgD","event":"stripe_attached","ts":"2026-08-22T17:24:25.457732Z"}],"invoice_id":"in-7d96eb219b65","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T17:24:22Z","period_start":"2026-08-22T16:24:22Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7IeaEPBjrbSOynxT7L7HgD","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T17:42:27.826091Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T17:42:27.826091Z"},{"detail":"stripe invoice in_1U7Iw4EPBjrbSOyn7xKOXxQE","event":"stripe_attached","ts":"2026-08-22T17:42:28.993854Z"}],"invoice_id":"in-ede817d8aa45","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T17:42:26Z","period_start":"2026-08-22T16:42:26Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7Iw4EPBjrbSOyn7xKOXxQE","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T17:52:26.957683Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T17:52:26.957683Z"},{"detail":"stripe invoice in_1U7J5jEPBjrbSOyn8ZKkbWaZ","event":"stripe_attached","ts":"2026-08-22T17:52:28.126477Z"}],"invoice_id":"in-6e61fee51069","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T17:52:25Z","period_start":"2026-08-22T16:52:25Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7J5jEPBjrbSOyn8ZKkbWaZ","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T18:06:53.544986Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T18:06:53.544986Z"},{"detail":"stripe invoice in_1U7JJiEPBjrbSOynERegFoS7","event":"stripe_attached","ts":"2026-08-22T18:06:54.635346Z"}],"invoice_id":"in-fc7c95a23060","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T18:06:52Z","period_start":"2026-08-22T17:06:52Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7JJiEPBjrbSOynERegFoS7","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T18:47:42.629650Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T18:47:42.629650Z"},{"detail":"stripe invoice in_1U7JxDEPBjrbSOyn9EY5MFlX","event":"stripe_attached","ts":"2026-08-22T18:47:43.829927Z"}],"invoice_id":"in-e8672897290b","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T18:47:40Z","period_start":"2026-08-22T17:47:40Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7JxDEPBjrbSOyn9EY5MFlX","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T19:06:48.114155Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T19:06:48.114155Z"},{"detail":"stripe invoice in_1U7KFgEPBjrbSOyngeE1L8au","event":"stripe_attached","ts":"2026-08-22T19:06:49.264590Z"}],"invoice_id":"in-e1319fe8bf7e","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T19:06:46Z","period_start":"2026-08-22T18:06:46Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7KFgEPBjrbSOyngeE1L8au","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T19:49:27.587113Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T19:49:27.587113Z"},{"detail":"stripe invoice in_1U7KuyEPBjrbSOyn9m78yRLZ","event":"stripe_attached","ts":"2026-08-22T19:49:28.758313Z"}],"invoice_id":"in-30cfe9c6c792","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T19:49:25Z","period_start":"2026-08-22T18:49:25Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7KuyEPBjrbSOyn9m78yRLZ","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T20:11:08.041970Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T20:11:08.041970Z"},{"detail":"stripe invoice in_1U7LFwEPBjrbSOynYtCL8KjX","event":"stripe_attached","ts":"2026-08-22T20:11:09.237806Z"}],"invoice_id":"in-e860fe4361cb","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T20:11:05Z","period_start":"2026-08-22T19:11:05Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7LFwEPBjrbSOynYtCL8KjX","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T20:26:47.572634Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T20:26:47.572634Z"},{"detail":"stripe invoice in_1U7LV6EPBjrbSOynPymSIveU","event":"stripe_attached","ts":"2026-08-22T20:26:48.839657Z"}],"invoice_id":"in-b9245bcc15bf","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T20:26:45Z","period_start":"2026-08-22T19:26:45Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7LV6EPBjrbSOynPymSIveU","tenant_name":"00000000000000000000000000000001","total_cents":1},{"created_at":"2026-08-22T22:37:35.706348Z","created_by":"000000000000000001","currency":"eur","customer_email":null,"history":[{"detail":"draft invoice created by 000000000000000001","event":"created","ts":"2026-08-22T22:37:35.706348Z"},{"detail":"stripe invoice in_1U7NXgEPBjrbSOyndPD6S516","event":"stripe_attached","ts":"2026-08-22T22:37:37.056837Z"}],"invoice_id":"in-081951f5a64a","lines":[{"amount_cents":1,"amount_eur":"0.0050","description":"Floating IPs — 1 address(es), 1.00 hours @ 0.005 EUR/h","key":"fip","quantity":1.0,"unit":"fip-hour","unit_price_eur":"0.005"}],"notify_events":[],"period_end":"2026-08-22T22:37:34Z","period_start":"2026-08-22T21:37:34Z","project_id":"00000000000000000000000000000001","status":"draft","stripe_customer_id":"cus_V5eG5UfDyhwiKU","stripe_invoice_id":"in_1U7NXgEPBjrbSOyndPD6S516","tenant_name":"00000000000000000000000000000001","total_cents":1}]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/usage/summary":{"get":{"tags":["w7-proxy"],"summary":"Proxy Usage Summary","description":"Metered usage for the caller's tenant (tier 0+), proxied to billing.\n\nTHE POINT IS THE BYPASS IT ENDS (2026-08-12). This resource lived only on\nthe billing service, so the CLI reached it by being pointed at billing\ndirectly (`ig1 config set-context --billing`) and the MCP did the same.\nEvery such shortcut is a second front door that has to re-implement the\ngateway's rules — tier gating, the audit trail, rate limiting — or\nquietly skip them, and skipping is what actually happens. The gateway\nforwards the caller's own bearer, billing applies its own tenant\nresolution, and there is one door again.","operationId":"proxy_usage_summary","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"period":{"end":"2026-08-23T04:26:07.097374+00:00","start":"2026-08-01T00:00:00+00:00"},"project_id":"00000000000000000000000000000001","rated":{"currency":"eur","lines":[{"amount_cents":61,"amount_eur":"0.61229180","description":"Floating IPs — 1 address(es), 122.46 hours @ 0.005 EUR/h","key":"fip","quantity":122.45836,"unit":"fip-hour","unit_price_eur":"0.005"}],"total_cents":61,"total_eur":"0.61"},"tenant_name":"ig1-customer-example-1","usage":{"floating_ips":[{"address":"10.168.210.192","created":"2026-08-18T01:58:37Z","fip_id":"b2ac4f2c-7c6c-4cb0-b14b-f68c4f3121bb","hours":122.45836038166667,"network_id":"a61e2f6c-176a-406d-80fe-af4fdd32c204","status":"DOWN"}],"instances":[],"period_end":"2026-08-23T04:26:07.097374Z","period_start":"2026-08-01T00:00:00Z","project_id":"00000000000000000000000000000001","totals":{"fip_hours":122.45836,"instance_seconds":0.0,"vcpu_seconds":0.0,"volume_gb_hours":0.0},"volumes":[]}}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/events":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Events","description":"Recent bus events (tier 0+; the budget-breach feed's source),\nproxied to the events service.","operationId":"proxy_list_events","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":137,"events":[{"at":"2026-08-22T22:42:20.037679Z","id":"795ba792-6f5b-4c0d-9554-407c336857ec","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:42:20Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-fca82e06e35849f2","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:43:19.965156Z","id":"030e1e9d-1341-4cc7-b305-c42f876b4be0","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:43:19Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-878336b112714d16","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:43:53.222969Z","id":"68748834-3c3d-43a7-8329-d3d4d79f566f","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:43:53Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-878336b112714d16","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:44:06.123372Z","id":"f9a2f932-aabb-4d66-ba24-df9f9f520de1","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-22T22:44:06Z","credential_id":"cred-7a85465ae17f4187","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:44:06.132586Z","id":"9426e24f-56d1-4207-8e1a-bfdf3dec963f","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:44:06Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:44:11.643842Z","id":"e8a11837-c778-415b-ac58-8b901e95c585","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-22T22:44:11Z","credential_id":"cred-7a85465ae17f4187","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:44:11.654427Z","id":"9388058d-dbd4-406e-8ca6-63b09551fceb","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:44:11Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-7a85465ae17f4187","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:44:54.833670Z","id":"d15d8dff-ca12-4ff0-840a-3272782e7863","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:44:54Z","credential_id":null,"method":"POST","operation_id":"invite_org_user","params_redacted":[],"path":"/v1/org/users/invite","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:11.939485Z","id":"7f49be7e-6cc8-48fd-b974-661f8686762b","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:11Z","credential_id":"cred-e1a2bdae17964443","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:45:11.944018Z","id":"21f0416a-05ab-4979-8e75-68004bab50be","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:11Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:12.610915Z","id":"54074de3-907b-43e4-ba91-a75b4bd96c11","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:12Z","credential_id":"cred-29e24d8f05aa4dbb","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:45:12.616727Z","id":"c954f2d9-7850-46ef-ba29-dedf7f34be4d","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:12Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:13.262309Z","id":"84e9df0e-1cbc-41f8-b29a-daa909f239ce","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:13Z","credential_id":"cred-ff601e78e42545f3","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:45:13.269793Z","id":"c0e67f00-bc96-4018-ac2b-8bccaef86527","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-22T22:45:13Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:16.491401Z","id":"c8bf51c9-0eb9-4b43-bc15-f92971c8545a","payload":{"action":"api.write","actor_user_id":"000000000000000012","at":"2026-08-22T22:45:16Z","credential_id":"cred-e1a2bdae17964443","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:47.518246Z","id":"ad5546dc-250d-4ef5-bbdd-4fc14a6b81ae","payload":{"action":"api.write","actor_user_id":"000000000000000013","at":"2026-08-22T22:45:47Z","credential_id":"cred-29e24d8f05aa4dbb","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:49.362834Z","id":"66ee7ccd-0b07-4540-9a2a-901cceccfd4c","payload":{"action":"api.write","actor_user_id":"000000000000000014","at":"2026-08-22T22:45:49Z","credential_id":"cred-ff601e78e42545f3","method":"DELETE","operation_id":"proxy_delete_budget","params_redacted":[],"path":"/v1/budgets/bdg-69987df77a33","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:54.791119Z","id":"07cd7ad0-4924-4a5d-88ac-6f62d350ae2f","payload":{"action":"api.write","actor_user_id":"000000000000000012","at":"2026-08-22T22:45:54Z","credential_id":"cred-e1a2bdae17964443","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:55.554205Z","id":"426aebba-c51c-4943-8478-c854410c6423","payload":{"action":"api.write","actor_user_id":"000000000000000014","at":"2026-08-22T22:45:55Z","credential_id":"cred-ff601e78e42545f3","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:58.750274Z","id":"7e60ecc3-1e60-40b3-be3d-91e6dccab485","payload":{"action":"api.write","actor_user_id":"000000000000000012","at":"2026-08-22T22:45:58Z","credential_id":"cred-e1a2bdae17964443","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:45:59.698328Z","id":"68e7a593-0bc0-4826-b126-5937f596248f","payload":{"action":"api.write","actor_user_id":"000000000000000013","at":"2026-08-22T22:45:59Z","credential_id":"cred-29e24d8f05aa4dbb","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":422},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:46:00.721966Z","id":"74db8844-c5ea-4b85-8603-b2d6ff6c2121","payload":{"action":"api.write","actor_user_id":"000000000000000013","at":"2026-08-22T22:46:00Z","credential_id":"cred-29e24d8f05aa4dbb","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:46:02.885538Z","id":"966f19c6-6f2e-403d-abf1-2b963a3b1508","payload":{"action":"api.write","actor_user_id":"000000000000000013","at":"2026-08-22T22:46:02Z","credential_id":"cred-29e24d8f05aa4dbb","method":"POST","operation_id":"proxy_test_webhook","params_redacted":[],"path":"/v1/webhooks/wh-efae6b294526/test","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:46:06.930656Z","id":"9d4ff523-8a94-4ac8-b878-ff183be6f821","payload":{"action":"api.write","actor_user_id":"000000000000000014","at":"2026-08-22T22:46:06Z","credential_id":"cred-ff601e78e42545f3","method":"DELETE","operation_id":"proxy_delete_webhook","params_redacted":[],"path":"/v1/webhooks/wh-efae6b294526","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-22T22:46:11.296176Z","id":"9aa1abe8-c76a-4108-be4c-cce1aa28d8bc","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-22T22:46:11Z","credential_id":"cred-e1a2bdae17964443","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:46:11.658879Z","id":"965fe76b-ca50-4c3f-b298-a318f90f1dee","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-22T22:46:11Z","credential_id":"cred-29e24d8f05aa4dbb","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-22T22:46:12.138143Z","id":"139351d8-fb83-48b3-9e82-71cf318e367c","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-22T22:46:12Z","credential_id":"cred-ff601e78e42545f3","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T01:57:19.079993Z","id":"4e5b1d50-4edd-4b2d-99f1-2691639699df","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T01:57:19Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T01:57:25.369141Z","id":"0ce4e143-4af4-415c-be38-6dee84f43695","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T01:57:25Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787450202","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:02:19.111320Z","id":"18671c69-fe6d-4c26-b614-e2bc877196c4","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:19Z","credential_id":"cred-07334bc44d384554","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T02:02:19.120871Z","id":"f3d0c7c2-72d3-4643-9817-c54016760b22","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:19Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:02:19.816124Z","id":"137c5e9a-eafd-4e2a-8fd0-8786e403608d","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:19Z","credential_id":"cred-2a361952a64f4276","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T02:02:19.824060Z","id":"42adfcc0-aee3-48a6-a62d-01d423ae4acc","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:19Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:02:20.476582Z","id":"8e93445a-2614-4d9d-be88-5615b9942624","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:20Z","credential_id":"cred-0d3933579c664d53","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T02:02:20.482012Z","id":"4e58b76c-ae0c-4a8a-8aa8-449592399ea3","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T02:02:20Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:02:23.757115Z","id":"1728a820-fe44-4a6c-9aa8-d52225150efe","payload":{"action":"api.write","actor_user_id":"000000000000000015","at":"2026-08-23T02:02:23Z","credential_id":"cred-07334bc44d384554","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:03.960403Z","id":"82980f19-dd40-46d0-ad51-d33a8738dd5a","payload":{"action":"api.write","actor_user_id":"000000000000000016","at":"2026-08-23T02:03:03Z","credential_id":"cred-2a361952a64f4276","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:05.426646Z","id":"9dbd9977-6f47-4f36-bd19-155447d774ec","payload":{"action":"api.write","actor_user_id":"000000000000000017","at":"2026-08-23T02:03:05Z","credential_id":"cred-0d3933579c664d53","method":"DELETE","operation_id":"proxy_delete_budget","params_redacted":[],"path":"/v1/budgets/bdg-f4febba1c1aa","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:11.193946Z","id":"3c7dc03d-a235-4fcc-b545-dd45765e042b","payload":{"action":"api.write","actor_user_id":"000000000000000015","at":"2026-08-23T02:03:11Z","credential_id":"cred-07334bc44d384554","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:12.048902Z","id":"cf222061-a780-4818-8f3b-b0f3b528ee3d","payload":{"action":"api.write","actor_user_id":"000000000000000017","at":"2026-08-23T02:03:12Z","credential_id":"cred-0d3933579c664d53","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:16.134194Z","id":"f2522295-3ff3-4a5a-a7e4-8b09aefe7774","payload":{"action":"api.write","actor_user_id":"000000000000000015","at":"2026-08-23T02:03:16Z","credential_id":"cred-07334bc44d384554","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:17.175104Z","id":"f78924c7-b3b9-433e-b649-f36b0f053cbf","payload":{"action":"api.write","actor_user_id":"000000000000000016","at":"2026-08-23T02:03:17Z","credential_id":"cred-2a361952a64f4276","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":422},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:18.439525Z","id":"dec5a031-8c5e-4c6e-bcda-1a3668e4a3e9","payload":{"action":"api.write","actor_user_id":"000000000000000016","at":"2026-08-23T02:03:18Z","credential_id":"cred-2a361952a64f4276","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:19.898738Z","id":"24e30b82-3a86-42de-8dc0-65898205ce95","payload":{"action":"api.write","actor_user_id":"000000000000000016","at":"2026-08-23T02:03:19Z","credential_id":"cred-2a361952a64f4276","method":"POST","operation_id":"proxy_test_webhook","params_redacted":[],"path":"/v1/webhooks/wh-70d32ef3b874/test","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:23.863233Z","id":"ed150add-67a9-4006-beb1-31450556c1f3","payload":{"action":"api.write","actor_user_id":"000000000000000017","at":"2026-08-23T02:03:23Z","credential_id":"cred-0d3933579c664d53","method":"DELETE","operation_id":"proxy_delete_webhook","params_redacted":[],"path":"/v1/webhooks/wh-70d32ef3b874","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T02:03:26.369507Z","id":"fb6b4045-8cd5-464c-b3f0-3921715a5187","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T02:03:26Z","credential_id":"cred-07334bc44d384554","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T02:03:26.677879Z","id":"4e107968-97a9-4e5d-9a85-3839a7ef82b0","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T02:03:26Z","credential_id":"cred-2a361952a64f4276","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T02:03:27.002406Z","id":"4fcd69fc-54fa-4bca-9dcd-c453f08a2a59","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T02:03:26Z","credential_id":"cred-0d3933579c664d53","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:11:22.347975Z","id":"cbab3e14-d2f7-4cce-b159-ba91ab4bc946","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:22Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:11:25.476354Z","id":"b3bf3342-54bc-4957-9e19-040a8acfe9f0","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:25Z","credential_id":"cred-74df7b8ca29e45ea","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:11:25.484179Z","id":"6d5fa679-4e04-4e01-a223-b5ab0beab8c4","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:25Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:11:34.456473Z","id":"49200b73-29bf-4a2b-b2e9-08d3329879ee","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:34Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787454637","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:11:34.741305Z","id":"b70ec5f3-d5d1-4e2a-89d1-7ffba00f26e9","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:34Z","credential_id":"cred-74df7b8ca29e45ea","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:11:34.748736Z","id":"ac7466a5-0b28-4913-a111-77a8600aa439","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:11:34Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-74df7b8ca29e45ea","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:13:28.265048Z","id":"b1dc75a6-5a08-438f-8e62-8a1bafc82de8","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:28Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:13:30.362681Z","id":"6ea6324c-f9c4-41fe-b35f-fd4a7fea1b19","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:30Z","credential_id":"cred-2978663c1eae4e71","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:13:30.371935Z","id":"bfbace82-b19a-4e25-a88b-768c1e8eb42e","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:30Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:13:44.969898Z","id":"9cf30ed5-8ada-459d-9e3b-ee28aaadafc7","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:44Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787454767","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:13:45.481354Z","id":"bbbc7947-991e-46c2-9222-22c2429c9b65","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:45Z","credential_id":"cred-2978663c1eae4e71","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:13:45.488105Z","id":"ec8215df-5560-48bf-bcba-5453ae0925f6","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:13:45Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-2978663c1eae4e71","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:15:28.015773Z","id":"c7d3eade-2834-43f7-8a2a-0ca6ab056bce","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:27Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:15:29.325985Z","id":"80c4ee54-66b2-4a47-80cc-fae71d90caca","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:29Z","credential_id":"cred-3dd06824ddb44013","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:15:29.334801Z","id":"b4342abd-cae6-4733-a12c-9ed405a211a0","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:29Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:15:39.367944Z","id":"eb1f00c4-4289-495e-b827-bc0e0ac99403","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:39Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787454887","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:15:39.713357Z","id":"52cc4ff6-d360-44b3-aa9a-0180189a14cf","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:39Z","credential_id":"cred-3dd06824ddb44013","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:15:39.720290Z","id":"656dfb61-56e5-4b09-bbda-91c963a0290b","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:15:39Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-3dd06824ddb44013","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:16:36.909252Z","id":"a30f6320-4f8a-46aa-9202-d885c50c1a1f","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:36Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:16:38.911302Z","id":"eca6cb93-669a-4c81-96f6-e52d52567349","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:38Z","credential_id":"cred-03d79a0166264724","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:16:38.925558Z","id":"17a4243a-147e-49cb-80a5-18205685e113","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:38Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:16:48.891822Z","id":"f37d2ecc-428a-4b35-9d6d-7854adf4b29c","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:48Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787454953","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:16:49.214498Z","id":"b4caf8d5-ca6b-4641-83b4-69e334725463","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:49Z","credential_id":"cred-03d79a0166264724","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:16:49.222790Z","id":"b0f25d12-ae8e-441a-99b9-aacfd3e71ba7","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:16:49Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-03d79a0166264724","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:21:34.366731Z","id":"451aea3e-17a0-4ce7-b8cc-50864f6ead19","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:21:34Z","credential_id":"cred-5004dad0b3b74d56","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:21:34.380039Z","id":"8a2f5f55-9922-4eed-9f91-fea8c02d1a66","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:21:34Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:22:06.215254Z","id":"789915f2-eba3-4524-8112-623a7cb26b04","payload":{"action":"api.write","actor_user_id":"000000000000000018","at":"2026-08-23T03:22:06Z","credential_id":"cred-5004dad0b3b74d56","method":"POST","operation_id":"proxy_openstack_post","params_redacted":[],"path":"/v1/openstack/compute/servers","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:22:31.602521Z","id":"ee44d37f-e89e-4218-a8e6-dd6c6dee556a","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:22:31Z","credential_id":"cred-5004dad0b3b74d56","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:22:31.610992Z","id":"cce35d10-c388-4316-80c6-e54baeeea96e","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:22:31Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-5004dad0b3b74d56","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:23:42.907719Z","id":"eca4078b-e733-49d5-a9a2-4f28e1aa8b36","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:23:42Z","credential_id":"cred-8c85c44b9bd448a6","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:23:42.921354Z","id":"f6e7dde1-750b-4f14-8281-4e9b4aac0e32","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:23:42Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:24:00.222901Z","id":"8b5f682a-551e-481a-b8a3-6b96f589cfe4","payload":{"action":"api.write","actor_user_id":"000000000000000019","at":"2026-08-23T03:24:00Z","credential_id":"cred-8c85c44b9bd448a6","method":"POST","operation_id":"proxy_openstack_post","params_redacted":[],"path":"/v1/openstack/network/v2.0/security-groups","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:24:23.268083Z","id":"32ca9aa6-c0a9-4e3b-bf54-a438363a1ea7","payload":{"action":"api.write","actor_user_id":"000000000000000019","at":"2026-08-23T03:24:23Z","credential_id":"cred-8c85c44b9bd448a6","method":"DELETE","operation_id":"proxy_openstack_delete","params_redacted":[],"path":"/v1/openstack/compute/v2.1/servers/00000000-0000-0000-0000-000000000000","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:24:41.590679Z","id":"5d10728e-d8a0-49ba-8e56-8fad0709b8a4","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:24:41Z","credential_id":null,"method":"DELETE","operation_id":"proxy_openstack_delete","params_redacted":[],"path":"/v1/openstack/network/v2.0/security-groups/c56b1d8a-6b63-4041-a8e7-9771733e98ac","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":204},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:25:00.238443Z","id":"6fe4618b-c7d6-4ea2-bcfd-90420393c03f","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:25:00Z","credential_id":"cred-8c85c44b9bd448a6","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:25:00.698169Z","id":"a8c5177d-d5a5-42ac-b6d2-3fba321e77a2","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:25:00Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-8c85c44b9bd448a6","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:25:22.767052Z","id":"ace53e06-2f03-40d3-8068-0eb97a451ba2","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:25:22Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-8c85c44b9bd448a6","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:26:42.691329Z","id":"caa94729-e421-47d4-ab6b-704144d79342","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:26:42Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-059cce9fa32244ab","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:27:13.712815Z","id":"56b00076-38df-4957-93b2-c7d6aaee4937","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:27:13Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-059cce9fa32244ab","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:27:25.211025Z","id":"6792944d-52be-48ec-9219-f745bd406481","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:27:25Z","credential_id":"cred-4b0cb69099e94461","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:27:25.219867Z","id":"c016eee2-90ea-4958-b867-3214c021cfcd","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:27:25Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:27:36.225796Z","id":"e09333af-7e44-4ab0-9dbe-ecf45f955b1a","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:27:36Z","credential_id":"cred-4b0cb69099e94461","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:27:36.232674Z","id":"1ee1f529-2cd7-40cc-b91c-2d859ea6bdcf","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:27:36Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-4b0cb69099e94461","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:28:34.937500Z","id":"1ceac047-793f-4363-87fb-d71c28ab8eae","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:28:34Z","credential_id":null,"method":"POST","operation_id":"invite_org_user","params_redacted":[],"path":"/v1/org/users/invite","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:30:21.738746Z","id":"b47fe9ad-ece3-4ccc-9a91-2a4cd0b26631","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:21Z","credential_id":null,"method":"POST","operation_id":"create_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:30:25.170987Z","id":"ecc2277e-2a70-4243-a954-9033e1640018","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:25Z","credential_id":"cred-27a3374883c54673","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:30:25.182862Z","id":"1b1b229c-4e73-49fa-b3d5-677c1faa2edc","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:25Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:30:36.693806Z","id":"6c13e713-1540-48c5-b28c-b4299dd691fc","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:36Z","credential_id":null,"method":"DELETE","operation_id":"delete_object_storage_bucket","params_redacted":[],"path":"/v1/object-storage/buckets/compat45-1787455772","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:30:37.264315Z","id":"ec29cc0f-4ed0-4c43-a89a-8c008dc05c2f","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:37Z","credential_id":"cred-27a3374883c54673","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:30:37.272586Z","id":"93c4b097-c957-4efb-8a31-5825bc2da05e","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:30:37Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-27a3374883c54673","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:45:37.277745Z","id":"28113d53-ae3a-4584-a986-70d08b904cfa","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:45:37Z","credential_id":"cred-b4af6094d7494290","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:45:37.290963Z","id":"9193e989-65fb-43be-bf4f-2c23e33f4a83","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:45:37Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:46:06.730363Z","id":"fc1effe1-a761-4e4b-8a77-38c78d28ba04","payload":{"action":"api.write","actor_user_id":"000000000000000020","at":"2026-08-23T03:46:06Z","credential_id":"cred-b4af6094d7494290","method":"POST","operation_id":"proxy_openstack_post","params_redacted":[],"path":"/v1/openstack/compute/servers","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:46:22.088716Z","id":"0b2790cd-054e-429b-9378-4f6c5bf4a358","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:46:22Z","credential_id":"cred-b4af6094d7494290","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:46:22.099077Z","id":"14b41568-19cd-48ea-9aef-117ee49d4316","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:46:22Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-b4af6094d7494290","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:46:36.507396Z","id":"3cdef920-0754-412f-aa5e-d52d891fff0f","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:46:36Z","credential_id":"cred-d1709664d3fb4369","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:46:36.514794Z","id":"a2eba2ac-cb0f-4ecc-bbdd-99ac0f48b5fc","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:46:36Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:46:46.108901Z","id":"d714afbe-877d-4831-923d-154f23272b5d","payload":{"action":"api.write","actor_user_id":"000000000000000021","at":"2026-08-23T03:46:46Z","credential_id":"cred-d1709664d3fb4369","method":"POST","operation_id":"proxy_openstack_post","params_redacted":[],"path":"/v1/openstack/network/v2.0/security-groups","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:46:57.013571Z","id":"d7b64975-d553-404f-b667-ff9a48d9b5d6","payload":{"action":"api.write","actor_user_id":"000000000000000021","at":"2026-08-23T03:46:56Z","credential_id":"cred-d1709664d3fb4369","method":"DELETE","operation_id":"proxy_openstack_delete","params_redacted":[],"path":"/v1/openstack/compute/v2.1/servers/00000000-0000-0000-0000-000000000000","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:47:02.585509Z","id":"3f988134-8382-4a85-a7f3-4a6658965ab6","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:47:02Z","credential_id":null,"method":"DELETE","operation_id":"proxy_openstack_delete","params_redacted":[],"path":"/v1/openstack/network/v2.0/security-groups/c8dadc8d-e4fa-488f-a033-ff49d6804b81","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":204},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:47:07.350469Z","id":"27ad43d2-545b-45dd-952f-718fbf999793","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:47:07Z","credential_id":"cred-d1709664d3fb4369","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:47:07.361357Z","id":"c848e52e-58a1-4e2b-8e76-b4cc8fefb720","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:47:07Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-d1709664d3fb4369","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:47:13.095684Z","id":"8a4c7e1b-aa0f-4cdb-9fba-25adafeb5e47","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:47:13Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-d1709664d3fb4369","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:48:23.546033Z","id":"11eaf360-2edb-408f-8131-ea3504dae102","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:23Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-409647c816e04a47","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:48:40.460883Z","id":"4ae2d5ad-cb95-4dfa-a863-558a3e99ec86","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:40Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-409647c816e04a47","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":404},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:48:50.880478Z","id":"9e83dec1-f0fd-4121-9ad8-378becfa6a7c","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:50Z","credential_id":"cred-91eeb010114a42a0","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:48:50.888250Z","id":"eb66e32a-0944-41f8-bc84-573e10bcdf60","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:50Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:48:58.419089Z","id":"4e68159f-7870-4ba0-b861-6351b26a8bcd","payload":{"action":"credential.revoked","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:58Z","credential_id":"cred-91eeb010114a42a0","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T03:48:58.429890Z","id":"cf5b4f90-8cb6-4ca1-9e0f-f3da7f24eefb","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:48:58Z","credential_id":null,"method":"DELETE","operation_id":"revoke_credential","params_redacted":[],"path":"/v1/credentials/cred-91eeb010114a42a0","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T03:49:44.993205Z","id":"5113ce95-d533-4b63-ba2f-fcc96e938fe4","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T03:49:44Z","credential_id":null,"method":"POST","operation_id":"invite_org_user","params_redacted":[],"path":"/v1/org/users/invite","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:20:37.246902Z","id":"a8cf8115-0075-455e-b366-2689ee38c69a","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:37Z","credential_id":"cred-80127f7956df44e4","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T04:20:37.256494Z","id":"196388d7-d4fd-432f-8976-134092beab8b","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:37Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:20:38.097669Z","id":"49ba9428-fdf7-4137-816a-b9a7cefeff9f","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:38Z","credential_id":"cred-0d2191538d7a4441","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T04:20:38.106408Z","id":"86155d8c-bc9c-4a5c-9f38-82bf7458d7fe","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:38Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:20:39.072020Z","id":"3736f16f-6c21-4a1b-b089-cbbc977a9b32","payload":{"action":"credential.created","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:39Z","credential_id":"cred-0b76672b191b49dd","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T04:20:39.080273Z","id":"39b826eb-76e0-4e98-af0f-b59490da9bda","payload":{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:39Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:20:42.722859Z","id":"533b7f23-414c-45c5-9224-98b9f4c272db","payload":{"action":"api.write","actor_user_id":"000000000000000022","at":"2026-08-23T04:20:42Z","credential_id":"cred-80127f7956df44e4","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:05.205891Z","id":"418adea1-7e81-4d30-bda8-1a5cb6549f6f","payload":{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:05Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:07.097555Z","id":"8d3c5559-d909-472a-80f2-f9233aefbdcb","payload":{"action":"api.write","actor_user_id":"000000000000000024","at":"2026-08-23T04:21:07Z","credential_id":"cred-0b76672b191b49dd","method":"DELETE","operation_id":"proxy_delete_budget","params_redacted":[],"path":"/v1/budgets/bdg-31ecac31c571","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:14.166541Z","id":"67a5fa88-f9d2-474c-bded-aa0a440d66f0","payload":{"action":"api.write","actor_user_id":"000000000000000022","at":"2026-08-23T04:21:14Z","credential_id":"cred-80127f7956df44e4","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:15.449995Z","id":"1490203f-08b9-40f9-b079-5656b95f7a88","payload":{"action":"api.write","actor_user_id":"000000000000000024","at":"2026-08-23T04:21:15Z","credential_id":"cred-0b76672b191b49dd","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:18.922493Z","id":"1da44b3d-62bd-42b7-b3b4-34415e2c0e2b","payload":{"action":"api.write","actor_user_id":"000000000000000022","at":"2026-08-23T04:21:18Z","credential_id":"cred-80127f7956df44e4","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:20.271750Z","id":"ba9da5c0-7dfe-4190-a38f-e687f311e93a","payload":{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:20Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":422},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:21.573681Z","id":"7ddd0f8a-2cfa-4886-99af-cb9f85e9f2d0","payload":{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:21Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:22.672605Z","id":"8aa1e2b7-a2a0-439e-9687-04da6e1fe61b","payload":{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:22Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_test_webhook","params_redacted":[],"path":"/v1/webhooks/wh-d59e5167e258/test","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:37.750071Z","id":"b8d19bb6-87af-471a-ad7d-5125eed44b37","payload":{"action":"api.write","actor_user_id":"000000000000000024","at":"2026-08-23T04:21:37Z","credential_id":"cred-0b76672b191b49dd","method":"DELETE","operation_id":"proxy_delete_webhook","params_redacted":[],"path":"/v1/webhooks/wh-d59e5167e258","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"api.audit"},{"at":"2026-08-23T04:21:40.306116Z","id":"041aaa9b-2758-4dc2-9dbc-d361bd38a6a5","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T04:21:40Z","credential_id":"cred-80127f7956df44e4","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":0},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T04:21:40.677215Z","id":"3c895589-5601-4de3-8b12-8d3f44892018","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T04:21:40Z","credential_id":"cred-0d2191538d7a4441","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":1},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"},{"at":"2026-08-23T04:21:41.033859Z","id":"b9caa320-5bb3-4dd2-9fa3-cec1ae78102c","payload":{"action":"credential.revoked","actor_user_id":"000000000000000001","at":"2026-08-23T04:21:41Z","credential_id":"cred-0b76672b191b49dd","kind":"api-key","project_id":"00000000000000000000000000000001","target_user_id":null,"tier":2},"source":"000000000000000001","tenant_id":"00000000000000000000000000000001","topic":"iam.credentials"}]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/webhooks":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Webhooks","description":"The tenant's webhooks (tier 0+), proxied to the events service.","operationId":"proxy_list_webhooks","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"webhooks":[]}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["w7-proxy"],"summary":"Proxy Create Webhook","description":"Create a webhook (tier 1+; the reveal-once secret passes through),\nproxied to the events service.","operationId":"proxy_create_webhook","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"webhooks":[]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/webhooks/{webhook_id}":{"delete":{"tags":["w7-proxy"],"summary":"Proxy Delete Webhook","description":"Delete a webhook (tier 2), proxied to the events service.","operationId":"proxy_delete_webhook","security":[{"HTTPBearer":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/deliveries":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Webhook Deliveries","description":"The per-webhook delivery log (tier 0+), proxied to events.","operationId":"proxy_list_webhook_deliveries","security":[{"HTTPBearer":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/test":{"post":{"tags":["w7-proxy"],"summary":"Proxy Test Webhook","description":"The test ping (tier 1+), proxied to the events service.","operationId":"proxy_test_webhook","security":[{"HTTPBearer":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events/topics":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Topics","description":"The registered topic list, proxied to the events service.\n\nServed rather than hardcoded in the console: a client carrying its own copy\ndrifts the day a topic is added, and the drift is silent — the user simply\nnever sees the new topic offered for subscription.","operationId":"proxy_list_topics","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":10,"topics":["agent.actions","api.audit","billing.budget.breached","iam.credentials","iam.operator.access","kaas.billing","kaas.cluster.health","kaas.cluster.lifecycle","platform.alerts","tenant.alarms"]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/integrations":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Integrations","description":"Notification destinations visible to the caller, proxied to events.","operationId":"proxy_list_integrations","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["w7-proxy"],"summary":"Proxy Create Integration","description":"Create a destination (scope decided by events.authorize_scope).","operationId":"proxy_create_integration","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/integrations/{integration_id}":{"patch":{"tags":["w7-proxy"],"summary":"Proxy Update Integration","description":"Edit a destination, proxied to the events service.","operationId":"proxy_update_integration","security":[{"HTTPBearer":[]}],"parameters":[{"name":"integration_id","in":"path","required":true,"schema":{"type":"string","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["w7-proxy"],"summary":"Proxy Delete Integration","description":"Delete a destination, proxied to the events service.","operationId":"proxy_delete_integration","security":[{"HTTPBearer":[]}],"parameters":[{"name":"integration_id","in":"path","required":true,"schema":{"type":"string","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/{integration_id}/deliveries":{"get":{"tags":["w7-proxy"],"summary":"Proxy List Integration Deliveries","description":"Recent delivery attempts for one destination, proxied to events.","operationId":"proxy_list_integration_deliveries","security":[{"HTTPBearer":[]}],"parameters":[{"name":"integration_id","in":"path","required":true,"schema":{"type":"string","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/{integration_id}/test":{"post":{"tags":["w7-proxy"],"summary":"Proxy Test Integration","description":"Send a synthetic event through the real delivery path, proxied.","operationId":"proxy_test_integration","security":[{"HTTPBearer":[]}],"parameters":[{"name":"integration_id","in":"path","required":true,"schema":{"type":"string","title":"Integration Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/signup":{"post":{"tags":["signups"],"summary":"Request Signup","description":"Public. Records an application; provisions NOTHING.\n\n202, not 201: nothing was created for the caller yet, and saying so\nhonestly sets the expectation that a human decides next.","operationId":"request_signup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/signups":{"get":{"tags":["signups"],"summary":"List Signups","operationId":"list_signups","security":[{"HTTPBearer":[]}],"parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"pending","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":4,"signups":[{"created_at":"2026-08-12T17:03:27Z","decided_at":null,"decided_by":null,"email":"user-09@example.com","id":"sgn-19ff6edcc4d-0db7","notes":{},"org_name":"Example Organisation","project_id":null,"reason":null,"state":"pending","tier":null},{"created_at":"2026-08-13T00:16:23Z","decided_at":null,"decided_by":null,"email":"user-13@example.com","id":"sgn-19ff87a28b0-1e44","notes":{},"org_name":"Example Organisation","project_id":null,"reason":null,"state":"pending","tier":null},{"created_at":"2026-08-13T03:25:56Z","decided_at":null,"decided_by":null,"email":"user-02@example.com","id":"sgn-19ff927b37a-2064","notes":{},"org_name":"Example Organisation","project_id":null,"reason":null,"state":"pending","tier":null},{"created_at":"2026-08-13T03:41:43Z","decided_at":null,"decided_by":null,"email":"user-06@example.com","id":"sgn-19ff9362679-b558","notes":{},"org_name":"Example Organisation","project_id":null,"reason":null,"state":"pending","tier":null}],"state":"pending"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/signups/capacity":{"get":{"tags":["signups"],"summary":"Signup Capacity","description":"What approving one more tenant would cost, per tier.\n\nThe operator queue must be able to show remaining budget BEFORE a\ndecision — \"can I say yes to this?\" is the actual question being asked.","operationId":"signup_capacity","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"live_tenants":{"discovery":3,"standard":1},"sellable":{"ram_mb":1000000.0,"storage_gib":4000.0,"storage_stored_gib":100.0,"storage_target_gib":1000.0,"vcpu":1000.0},"tiers":{"discovery":{"reasons":[],"would_fit":true},"extension":{"reasons":[],"would_fit":true},"standard":{"reasons":[],"would_fit":true},"suspended":{"reasons":[],"would_fit":true}}}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/signups/{signup_id}/approve":{"post":{"tags":["signups"],"summary":"Approve Signup","description":"Admin. Runs the real onboarding, then records the decision.\n\nORDER MATTERS: provision FIRST, mark approved SECOND. If provisioning\nfails the signup stays pending and can be retried; marking it approved\nfirst would strand an applicant in a state with no tenant behind it and no\nway back to the queue.","operationId":"approve_signup","security":[{"HTTPBearer":[]}],"parameters":[{"name":"signup_id","in":"path","required":true,"schema":{"type":"string","title":"Signup Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/signups/{signup_id}/reject":{"post":{"tags":["signups"],"summary":"Reject Signup","operationId":"reject_signup","security":[{"HTTPBearer":[]}],"parameters":[{"name":"signup_id","in":"path","required":true,"schema":{"type":"string","title":"Signup Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/registry/repositories":{"get":{"tags":["registry"],"summary":"List Repositories","description":"The caller's repositories (tier 0+).\n\nBuilt from the catalogue and FILTERED here rather than by handing the\ncaller a `registry:catalog:*` scope — that scope lists every namespace on\nthe platform, including other customers'.","operationId":"list_registry_repositories","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"namespace":"00000000000000000000000000000001","registry":"registry.10.57.8.64.nip.io","repositories":[]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/registry/repositories/{repository}/tags":{"get":{"tags":["registry"],"summary":"List Tags","description":"One repository's tags (tier 0+). A repository outside the caller's\nnamespace answers 404 — existence is not leaked across tenants (the\nphase-26 credential rule).","operationId":"list_registry_tags","security":[{"HTTPBearer":[]}],"parameters":[{"name":"repository","in":"path","required":true,"schema":{"type":"string","title":"Repository"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/registry/repositories/{repository}/tags/{tag}":{"delete":{"tags":["registry"],"summary":"Delete Tag","description":"Delete one tag's manifest (tier 2).\n\nDistribution deletes by DIGEST, not by tag, so this resolves the tag to\nits manifest digest first. NOTE the consequence, which is Distribution's\nand not this platform's: deleting a manifest removes EVERY tag pointing\nat that same digest. The response says which ones went.","operationId":"delete_registry_tag","security":[{"HTTPBearer":[]}],"parameters":[{"name":"repository","in":"path","required":true,"schema":{"type":"string","title":"Repository"}},{"name":"tag","in":"path","required":true,"schema":{"type":"string","title":"Tag"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/registry/usage":{"get":{"tags":["registry"],"summary":"Registry Usage","description":"Repository count against the caller's tier ceiling (tier 0+).","operationId":"registry_usage","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"login_hint":"docker login registry.10.57.8.64.nip.io -u <credential id> -p <secret>","namespace":"00000000000000000000000000000001","registry":"registry.10.57.8.64.nip.io","repositories":0,"repositories_limit":100}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/account":{"get":{"tags":["account"],"summary":"Get Account","description":"The caller's account identity. ensure_account doubles as migration:\na pre-existing tenant mints its number on first read, so no account is\never shown empty. Fail-SOFT — a Keystone hiccup degrades to the raw\nproject id, never a 500: identity display is not authorization.\n\nThe project is derived from the credential. ``?project=`` is an\nadmin-only override (constant-shape 403 for everyone else); a\nplatform-admin credential with NO override gets a 409 naming it, because\nsuch a credential is scoped to no tenant and therefore has no account.","operationId":"get_account","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only: read another project's account identity","title":"Project"},"description":"Platform admins only: read another project's account identity"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"account_number":"012345678901","alias":"example","name":"Example Organisation","project_id":"00000000000000000000000000000001","project_name":"ig1-customer-example-1"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/account/alias":{"put":{"tags":["account"],"summary":"Put Alias","operationId":"set_account_alias","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AliasUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/quotas":{"get":{"tags":["quotas"],"summary":"Get Quotas","description":"The caller's own limits and usage, with the tier that set them.\n\nTier 0. The project is derived from the credential; `?project=` is an\nadmin-only override and is refused with a constant-shape 403 for everyone\nelse, regardless of whether the project exists.","operationId":"get_quotas","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins on the OPERATOR plane only: read another project's quotas. Refused with a constant-shape 403 everywhere else, whether or not the project exists.","title":"Project"},"description":"Platform admins on the OPERATOR plane only: read another project's quotas. Refused with a constant-shape 403 everywhere else, whether or not the project exists."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"block_storage":{"backups":{"limit":0,"used":0},"gigabytes":{"limit":700,"used":0},"snapshots":{"limit":60,"used":0},"volumes":{"limit":100,"used":0}},"compute":{"cores":{"limit":128,"used":0},"instances":{"limit":60,"used":0},"key_pairs":{"limit":100},"ram_mb":{"limit":262144,"used":0},"server_groups":{"limit":20,"used":0}},"network":{"floating_ips":{"limit":16},"networks":{"limit":24},"ports":{"limit":768},"routers":{"limit":8},"security_group_rules":{"limit":400},"security_groups":{"limit":40},"subnets":{"limit":48}},"platform":{"credentials":{"limit":50},"kaas":{"clusters":{"limit":8},"worker_nodes":{"limit":30}},"object_storage":{"buckets":{"enforced":100,"limit":100,"used":1},"enforcement":{"enabled":true,"matches_tier":true,"source":"rgw"},"gigabytes":{"enforced":300,"limit":300,"used":0.0},"objects":{"enforced":20000000,"limit":20000000,"used":0}}},"project_id":"00000000000000000000000000000001","tier":{"description":null,"name":"extension","provisions_project":true}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tiers":{"get":{"tags":["quotas"],"summary":"List Tiers","description":"The quota-tier catalogue: every tier's name, display strings and\nheadline ceilings (B5 — the catalogue existed only as a yml file and an\noperator's memory; a customer deciding what to ask for had nothing to\nread).\n\nTier 0, any authenticated caller. Catalogue-only BY CONSTRUCTION:\ntiers.catalogue() is an allow-list projection, so the budget internals\n(measured capacity, reserves, oversubscription, planning caps) cannot\nreach this response by someone adding a key to the yml. Changing a tier\nremains the admin-only POST below.","operationId":"list_tiers","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":5,"tiers":[{"display":{"en":"Pending","fr":"En attente"},"name":"pending","provisions_project":false},{"block_storage":{"gigabytes":80,"snapshots":5,"volumes":8},"compute":{"cores":8,"instances":5,"ram_mb":16384},"credentials":5,"display":{"en":"Discovery","fr":"Découverte"},"edge_exposures":1,"kaas":{"clusters":1,"worker_nodes":3},"name":"discovery","network":{"floating_ips":2,"networks":3,"routers":2,"security_groups":10},"object_storage":{"buckets":5,"gigabytes":20,"objects":500000},"provisions_project":true},{"block_storage":{"gigabytes":300,"snapshots":20,"volumes":32},"compute":{"cores":32,"instances":20,"ram_mb":65536},"credentials":20,"display":{"en":"Standard","fr":"Standard"},"edge_exposures":4,"kaas":{"clusters":3,"worker_nodes":10},"name":"standard","network":{"floating_ips":8,"networks":12,"routers":4,"security_groups":20},"object_storage":{"buckets":25,"gigabytes":100,"objects":5000000},"provisions_project":true},{"block_storage":{"gigabytes":700,"snapshots":60,"volumes":100},"compute":{"cores":128,"instances":60,"ram_mb":262144},"credentials":50,"display":{"en":"Extension","fr":"Extension"},"edge_exposures":10,"kaas":{"clusters":8,"worker_nodes":30},"name":"extension","network":{"floating_ips":16,"networks":24,"routers":8,"security_groups":40},"object_storage":{"buckets":100,"gigabytes":300,"objects":20000000},"provisions_project":true},{"block_storage":{"gigabytes":0,"snapshots":0,"volumes":0},"compute":{"cores":0,"instances":0,"ram_mb":0},"credentials":0,"display":{"en":"Suspended","fr":"Suspendu"},"edge_exposures":0,"kaas":{"clusters":0,"worker_nodes":0},"name":"suspended","network":{"floating_ips":0,"networks":0,"routers":0,"security_groups":0},"object_storage":{"buckets":0,"gigabytes":0,"objects":0},"provisions_project":true}]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/projects":{"get":{"tags":["projects"],"summary":"The projects this account holds","description":"Every project in the caller's account, and the ceiling its tier grants.\n\nTier 0. This is the list `X-IG1-Project` selects from, and the answer to\n\"how many more may I create\" — a client that has to attempt a create to\ndiscover the ceiling is a client that discovers it by being refused.","operationId":"list_projects_v1_projects_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["projects"],"summary":"Add a project to this account","description":"Provision another isolated project in the caller's own account.\n\nThe new project is a full boundary — its own quota, network, object-storage\nnamespace and Kubernetes namespace — provisioned at the ACCOUNT's tier, and\nit becomes an entitlement of the calling identity, so `X-IG1-Project` can\nselect it immediately.\n\nRefused with 409 when the account already holds every project its tier\ngrants. That ceiling is the reason this can be a customer verb at all, and\nraising it is an admin act (`ig1 tenant set-tier`).","operationId":"create_project_v1_projects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/projects/{project_id}/contents":{"get":{"tags":["projects"],"summary":"What deleting this project would destroy","description":"A read-only survey, per resource kind. Tier 0.\n\nThis exists so that consent means something. A customer asked \"are you\nsure?\" is being asked to agree with a word; a customer shown \"3 instances,\n2 volumes, 1 bucket\" is being asked to agree with their own estate.\n\n`complete: false` means a service could not be READ — the counts below it\nare a floor, not a total. A client must say so rather than render the\npartial list as the whole truth, because that is the sentence that makes\nsomebody consent to destroying more than they were shown.","operationId":"project_contents_v1_projects__project_id__contents_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/projects/{project_id}":{"delete":{"tags":["projects"],"summary":"Delete a project and everything in it","description":"Empty the project, then remove it. Tier 2 and an owning role.\n\nIRREVERSIBLE, and refused rather than half-done. Every precondition is\nchecked before anything is destroyed, and the Keystone project is removed\nonly once the purge can prove the project is empty — a project deleted over\nthe top of live resources leaves them with no owner, no console entry and\nno bill, which is the one outcome worse than a failed delete.\n\nRefused with 409 for the account's LAST project (that is\n`POST /v1/account/close`), for the project this session is currently using,\nand while any Kubernetes cluster remains.","operationId":"delete_project_v1_projects__project_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDelete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/account/close":{"post":{"tags":["account"],"summary":"Close this account and destroy every project in it","description":"End the commercial relationship. Tier 2 and the Propriétaire ONLY.\n\nSTRICTER THAN DELETING A PROJECT, and the reason is the customer's own org\nchart rather than ours: the Propriétaire owns billing and an Administrateur\nholds billing read-only, so ending the relationship is not the\nAdministrateur's to decide. Deleting one project among several is an\noperational act; this is a contractual one.\n\nTWO CONFIRMATIONS, because they are two facts. `confirm` is the account\nnumber typed exactly — dashes optional, since that is how the console\ndisplays it. `confirm_projects` acknowledges separately that every project\nand everything inside them is destroyed: people who intend to close an\naccount do not always know it takes their projects with it, and a single\ncheckbox cannot tell those two intentions apart.\n\nNOT ATOMIC, and it cannot be — eight services and no distributed\ntransaction. It stops at the first project it cannot empty and says which\none, leaving the rest of the account intact rather than partly dismantled\nbehind a success message. Re-running is safe: the projects already removed\nare simply not there any more.","operationId":"close_account_v1_account_close_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountClose"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/org/ous":{"get":{"tags":["organization"],"summary":"The account's organizational units","description":"The OU tree, and which one each project sits in. Tier 0.","operationId":"list_ous_v1_org_ous_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["organization"],"summary":"Create an organizational unit","operationId":"create_ou_v1_org_ous_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OuCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/org/ous/{path}":{"delete":{"tags":["organization"],"summary":"Delete an empty organizational unit","description":"Refused while anything still hangs off it — a child OU, a project, or a\npolicy. Deleting a node whose children survive would orphan them into a\ntree position that no longer exists, and a policy on a vanished OU applies\nto nothing while still reading as protection (orgpolicy.validate_policy\nrefuses to CREATE that state; this stops it being reached by deletion).","operationId":"delete_ou_v1_org_ous__path__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/org/policies":{"get":{"tags":["organization"],"summary":"The account's organization policies","description":"Tier 0 — knowing the rules you are subject to is not a privilege.","operationId":"list_policies_v1_org_policies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["organization"],"summary":"Attach a deny-only policy to the account or an OU","description":"A policy sets a MAXIMUM; it never grants.\n\n`max_tier` composes by minimum down the tree and `deny` rules accumulate,\nso attaching one can only ever reduce what is reachable below it. The\n`/v1/org` surface itself is never deniable — see orgpolicy's anti-lockout\nnote.","operationId":"create_policy_v1_org_policies_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/org/policies/{policy_id}":{"delete":{"tags":["organization"],"summary":"Remove an organization policy","operationId":"delete_policy_v1_org_policies__policy_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"policy_id","in":"path","required":true,"schema":{"type":"string","title":"Policy Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/org/effective":{"get":{"tags":["organization"],"summary":"The guardrails in force for this caller","description":"What is actually in force, and which policies produced it.\n\nTier 0 and no role. A ceiling a caller cannot see is indistinguishable from\na bug — \"why is my DELETE a 403\" has to be answerable by the person holding\nthe token. `tier_without_org` is included so the effect is visible as a\nDIFFERENCE rather than as a number the caller has to compare against\nsomething they cannot read.","operationId":"effective_policy_v1_org_effective_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/audit":{"get":{"tags":["audit"],"summary":"Get Audit Trail","description":"The caller's write trail (tier 0), newest first.\n\nOne row per completed POST/PUT/PATCH/DELETE: actor, credential, tenant,\nmethod, path, operation id, result status. Bodies are never recorded and\nquery parameters appear as NAMES only — see ig1_api/audit.py for why\nthat is a hard rule on this particular surface rather than a preference.","operationId":"get_audit_trail","security":[{"HTTPBearer":[]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 lower bound on the event timestamp (default: 24h ago)","title":"Since"},"description":"ISO-8601 lower bound on the event timestamp (default: 24h ago)"},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 upper bound (default: now)","title":"Until"},"description":"ISO-8601 upper bound (default: now)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Rows to return, newest first (max 500)","default":100,"title":"Limit"},"description":"Rows to return, newest first (max 500)"},{"name":"method","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to one write method (POST/PUT/PATCH/DELETE)","title":"Method"},"description":"Filter to one write method (POST/PUT/PATCH/DELETE)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter to one result status code (e.g. 403)","title":"Status"},"description":"Filter to one result status code (e.g. 403)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"buffer":{"capacity":2000,"durable_topic":"api.audit","kept":10},"count":9,"events":[{"action":"api.write","actor_user_id":"000000000000000024","at":"2026-08-23T04:21:37Z","credential_id":"cred-0b76672b191b49dd","method":"DELETE","operation_id":"proxy_delete_webhook","params_redacted":[],"path":"/v1/webhooks/wh-d59e5167e258","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:22Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_test_webhook","params_redacted":[],"path":"/v1/webhooks/wh-d59e5167e258/test","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":200},{"action":"api.write","actor_user_id":"000000000000000023","at":"2026-08-23T04:21:20Z","credential_id":"cred-0d2191538d7a4441","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":422},{"action":"api.write","actor_user_id":"000000000000000022","at":"2026-08-23T04:21:18Z","credential_id":"cred-80127f7956df44e4","method":"POST","operation_id":"proxy_create_webhook","params_redacted":[],"path":"/v1/webhooks","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},{"action":"api.write","actor_user_id":"000000000000000024","at":"2026-08-23T04:21:15Z","credential_id":"cred-0b76672b191b49dd","method":"POST","operation_id":"create_status_incident","params_redacted":[],"path":"/v1/status/incidents","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},{"action":"api.write","actor_user_id":"000000000000000022","at":"2026-08-23T04:20:42Z","credential_id":"cred-80127f7956df44e4","method":"POST","operation_id":"proxy_create_budget","params_redacted":[],"path":"/v1/budgets","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":403},{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:39Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:38Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201},{"action":"api.write","actor_user_id":"000000000000000002","at":"2026-08-23T04:20:37Z","credential_id":null,"method":"POST","operation_id":"create_credential","params_redacted":[],"path":"/v1/credentials","project_id":"00000000000000000000000000000001","source":"ig1-api","status_code":201}],"limit":100,"since":"2026-08-22T04:25:54Z","until":"2026-08-23T04:25:54Z"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/metrics/instances/{server_id}/history":{"get":{"tags":["metrics"],"summary":"Get Instance Metrics History","description":"CPU and memory history for one instance (tier 0, phase 57).\n\nWindow as unix seconds (``since``/``until``, defaults: the last hour),\n``step`` in seconds (default 60). Series: ``cpu_busy_cores`` (vCPUs\nbusy, from the vCPU time rate), ``memory_rss_bytes`` and\n``memory_used_percent`` — the same two metric families the alarm\nevaluator samples live, so a threshold a customer sets and the graph\nthey see mean the same thing. A server that is not the caller's\nanswers 404 — ownership is settled by the join, before any range\nquery (module docstring).","operationId":"get_instance_metrics_history","security":[{"HTTPBearer":[]}],"parameters":[{"name":"server_id","in":"path","required":true,"schema":{"type":"string","title":"Server Id"}},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Since"}},{"name":"until","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Until"}},{"name":"step","in":"query","required":false,"schema":{"type":"integer","default":60,"title":"Step"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/metrics/instances/{server_id}":{"get":{"tags":["metrics"],"summary":"Get Instance Metrics","description":"CPU / memory / disk / NIC metrics for one instance (tier 0).\n\nTwo diagnostics samples about a second apart: the response carries the\nRAW cumulative counters and a ``cpu_percent`` computed from the delta\n(a single counter reading is not a rate — see ig1_api/metrics.py).\n\nDEPLOYMENT PREREQUISITE. Nova ships\n``os_compute_api:os-server-diagnostics`` as ADMIN-ONLY\n(``rule:context_is_admin``), so on an un-overridden cloud this endpoint\nanswers with Nova's own 403 for every customer credential. The override\nis a kolla config change, not something to work around here: it lives in\n``ansible/files/kolla/nova-policy.yaml`` (rule relaxed to\n``role:reader and project_id:%(project_id)s``) and is applied by\n``ansible/playbooks/phase-39-nova-diagnostics-policy.yml``. Until that\nhas run against a cloud, the upstream 403 surfaces VERBATIM — which\ntells an operator exactly what to fix. A faked 501 or an empty document\nwould not, and would look like a bug in this code instead.\n\nWHY VERBATIM DOES NOT LEAK EXISTENCE HERE. CLAUDE.md §3 requires\ncross-tenant lookups to answer 404, because a 403 confirms the resource\nexists. Both of Nova's refusals are safe under that rule: a server id\nbelonging to another project fails Nova's PROJECT-SCOPED instance lookup\nbefore policy is ever evaluated, so it returns 404 like any unknown id;\nand the policy 403 above is uniform across every id, existing or not, so\nit discloses nothing about any particular server. If Nova's lookup order\never changed so that a cross-project id produced a 403, this passthrough\nwould have to translate it — that is the condition to watch.","operationId":"get_instance_metrics","security":[{"HTTPBearer":[]}],"parameters":[{"name":"server_id","in":"path","required":true,"schema":{"type":"string","title":"Server Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alarms":{"get":{"tags":["alarms"],"summary":"List Alarms","description":"The caller's alarms and where each one's state machine stands (tier 0).\n\n``evaluator`` reports the interval and the fact that ``insufficient_data``\nis a real state, because a customer whose alarms are all sitting in it\ndeserves to be told that from the endpoint rather than left to guess that\nthe feature is broken — on this platform today they all are, until\nansible/playbooks/phase-39-nova-diagnostics-policy.yml has run.","operationId":"list_alarms","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"alarms":[],"count":0,"evaluator":{"interval_seconds":60.0,"metrics":["cpu_percent","memory_rss_kb","memory_actual_kb"],"states":["ok","alarm","insufficient_data"]}}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["alarms"],"summary":"Create Alarm","description":"Create an alarm (tier 1).\n\nIt is born ``insufficient_data`` and stays there until the evaluator has\nactually read the metric — never ``ok``, which would be an assurance\nnothing has earned yet.\n\nA caller with no resolved tenant is refused: an alarm with no owner is\ninvisible to its creator (owned_alarms treats an unstamped row as\nadmin-only) and would evaluate against a project that cannot be resolved.","operationId":"create_alarm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlarmCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"alarms":[],"count":0,"evaluator":{"interval_seconds":60.0,"metrics":["cpu_percent","memory_rss_kb","memory_actual_kb"],"states":["ok","alarm","insufficient_data"]}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/alarms/{alarm_id}":{"get":{"tags":["alarms"],"summary":"Get Alarm","description":"One alarm of the CALLER'S tenant (tier 0) — another tenant's alarm id\nanswers 404, exactly like an id that does not exist.","operationId":"get_alarm","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alarm_id","in":"path","required":true,"schema":{"type":"string","title":"Alarm Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["alarms"],"summary":"Delete Alarm","description":"Delete an alarm (tier 2).\n\nTier 2 rather than tier 1 because deleting an alarm is how monitoring gets\nturned off: a leaked operate-tier credential must not be able to blind the\ntenant before doing whatever it does next.\n\nOwnership is re-checked INSIDE the compare-and-swap, not only by the read\nabove: between the two, the other replica may have written the map, and a\ndelete that authorized against a stale copy would be authorizing against\nsomething it is no longer removing.","operationId":"delete_alarm","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alarm_id","in":"path","required":true,"schema":{"type":"string","title":"Alarm Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/edge/exposures":{"get":{"tags":["edge"],"summary":"List Exposures","description":"The caller's exposures with DERIVED status (tier 0): ``active`` only\nwhen the edge acknowledged a config at least as new as the row —\n``pending`` is the honest answer between a mutation and the sync.","operationId":"list_edge_exposures","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"exposures":[],"gateway":{"synced":false,"tls":"TLS terminates at the edge with an internal-CA wildcard certificate — install the lab CA (docs: /docs). Public certificates arrive with the delegated DNS zone (remediation 3.6) as a config swap.","vip":"10.57.8.75","wildcard":"*.10.57.8.75.nip.io"}}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["edge"],"summary":"Create Exposure","description":"Publish one hostname on the .75 edge (tier 1).\n\nThe gate order is cheapest-first and every refusal is specific:\n  1. the caller must HAVE a tenant (an unstamped exposure is unroutable\n     and invisible to its creator — fail closed);\n  2. the target must be plausible at all (not fabric, not off-platform);\n  3. the tenant's tier quota and the platform ceiling must have room —\n     both refusals state the numbers;\n  4. the name must be free WITHIN the tenant;\n  5. Neutron — under the caller's own credential — must confirm the\n     project owns the address. Unverifiable is 503, never a grant.\nCollision and quota are re-checked INSIDE the compare-and-swap: between\nthe pre-check and the write, the other replica may have admitted a row.","operationId":"create_edge_exposure","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExposureCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"exposures":[],"gateway":{"synced":false,"tls":"TLS terminates at the edge with an internal-CA wildcard certificate — install the lab CA (docs: /docs). Public certificates arrive with the delegated DNS zone (remediation 3.6) as a config swap.","vip":"10.57.8.75","wildcard":"*.10.57.8.75.nip.io"}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/edge/resync":{"post":{"tags":["edge"],"summary":"Resync Edge","description":"Re-render the edge configuration from the stored exposures (PLATFORM\nADMIN ONLY).\n\nWHY THIS EXISTS. The store caches the rendered haproxy document and\nrefreshes it only when the exposures change — so upgrading the API upgrades\nthe RENDERER without reaching the edge. Found live on 2026-08-23: api\n0.23.2 shipped the .77 public front door, phase 42 ran green, and the\ncontrollers went on installing a document the previous renderer had\nproduced, because nobody had created an exposure in between.\n\n`update_store` now self-heals on any write attempt, and this route is that\nattempt made deliberate: one call after a deploy, instead of waiting for a\ncustomer to happen to publish something. It is idempotent and converges —\ncalling it twice writes once.\n\nIt does NOT bump the generation: the routing table is unchanged, so every\nexposure stays `active` rather than being told it is behind. Run phase 42\nafterwards to install the refreshed document.\n\nAdmin-only because it rewrites the document EVERY tenant's traffic rides.","operationId":"resync_edge_config","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/edge/exposures/{exposure_id}":{"get":{"tags":["edge"],"summary":"Get Exposure","description":"One exposure of the CALLER'S tenant (tier 0) — another tenant's id\nanswers 404, exactly like an id that does not exist.","operationId":"get_edge_exposure","security":[{"HTTPBearer":[]}],"parameters":[{"name":"exposure_id","in":"path","required":true,"schema":{"type":"string","title":"Exposure Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["edge"],"summary":"Delete Exposure","description":"Un-publish one exposure (tier 2 — this takes a public hostname down).\n\nOwnership is re-checked INSIDE the compare-and-swap, not only by the read\nabove: between the two, the other replica may have rewritten the map, and\na delete authorized against a stale copy would be authorizing against\nsomething it is no longer removing.","operationId":"delete_edge_exposure","security":[{"HTTPBearer":[]}],"parameters":[{"name":"exposure_id","in":"path","required":true,"schema":{"type":"string","title":"Exposure Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/edge/exposures/{exposure_id}/domain":{"post":{"tags":["edge"],"summary":"Claim Domain","description":"Claim (or replace) the custom domain on one exposure (tier 1).\n\nIdempotent on the SAME domain: re-claiming what is already claimed returns\nthe current state untouched. That matters because the alternative —\nrotating the token and clearing the verification — would turn a retried\nrequest, or a `terraform apply` with no diff, into an outage of a name\nthat was working a second earlier.","operationId":"claim_edge_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"exposure_id","in":"path","required":true,"schema":{"type":"string","title":"Exposure Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainClaim"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["edge"],"summary":"Release Domain","description":"Release the custom domain (tier 2 — this takes a live hostname down).\n\nThe exposure and its derived `*.nip.io` hostname SURVIVE: a custom domain\nis additive, so releasing it is un-publishing one name, not deleting the\napp. The domain becomes claimable by anyone again, which is the point —\na customer who lets their registration lapse must not hold our routing\ntable hostage.","operationId":"release_edge_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"exposure_id","in":"path","required":true,"schema":{"type":"string","title":"Exposure Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/edge/exposures/{exposure_id}/verify":{"post":{"tags":["edge"],"summary":"Verify Domain","description":"Prove the claim: look up `_ig1-challenge.<domain> TXT` (tier 1).\n\nIdempotent, and cheap when already verified — an already-proved domain\nreturns immediately WITHOUT writing. Re-stamping the timestamp would bump\nthe store generation, and a generation the edge has not applied reads as\n``pending``: a re-verify that took a working exposure out of ``active``\nfor nothing would be the worst kind of no-op.\n\nFailure modes are three genuinely different answers and are never\ncollapsed: no resolver answered is 503 (ours), the record is absent or\nholds something else is 400 with the exact record to create (theirs), and\nno domain claimed at all is 400 pointing at the claim route.","operationId":"verify_edge_domain","security":[{"HTTPBearer":[]}],"parameters":[{"name":"exposure_id","in":"path","required":true,"schema":{"type":"string","title":"Exposure Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/access/policy":{"get":{"tags":["operator-access"],"summary":"Get Access Policy","description":"This project's rule for operator access (tier 0).\n\nA tenant with no stored policy reads as ``approval_required`` — absence of\na recorded instruction is never permission — and ``configured`` says which\nof the two it is, so the portal can offer \"choose how IG1 may support you\"\nrather than presenting a default as a decision the customer made.","operationId":"get_access_policy","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"configured":false,"effective_mode":"approval_required","expired":false,"max_grant_seconds":{"break_glass":3600,"customer_approved":28800},"modes":["approval_required","standing","denied"],"policy":{"expires_at":null,"mode":"approval_required","note":"","scope":"read","set_at":"","set_by":"","tenant_id":"00000000000000000000000000000001"},"scopes":["read","operate"]}}}}},"security":[{"HTTPBearer":[]}]},"put":{"tags":["operator-access"],"summary":"Set Access Policy","description":"Set this project's rule (tier 2 — Propriétaire / Administrateur).\n\nCHANGING THE RULE ALSO CLOSES WHAT THE OLD RULE OPENED. Two cases, and\nboth are the difference between consent and a form:\n\n  * switching to ``denied`` while a grant is live would otherwise leave an\n    operator inside for up to APPROVED_MAX_SECONDS after the customer said\n    \"no, stop\" — the mode would be a promise about the future only;\n  * leaving ``standing`` withdraws the instruction the standing grants were\n    minted under, so those grants go with it. Grants the customer approved\n    one by one are untouched: those were separate, explicit decisions.\n\nEvery closed grant is stamped revoked (never silently dropped) and emits\ngrant.revoked, so the customer's own log shows what their change did.","operationId":"set_access_policy","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"configured":false,"effective_mode":"approval_required","expired":false,"max_grant_seconds":{"break_glass":3600,"customer_approved":28800},"modes":["approval_required","standing","denied"],"policy":{"expires_at":null,"mode":"approval_required","note":"","scope":"read","set_at":"","set_by":"","tenant_id":"00000000000000000000000000000001"},"scopes":["read","operate"]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/access/requests":{"get":{"tags":["operator-access"],"summary":"List Access Requests","description":"Every grant touching this project, newest first (tier 0).\n\nNot just the pending ones: the question a customer asks of this page is\n\"who has been in here\", and a queue that showed only what is waiting for\nthem would answer a different, much more comfortable question.","operationId":"list_access_requests","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"active":0,"count":0,"pending":0,"requests":[]}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/access/requests/{grant_id}/approve":{"post":{"tags":["operator-access"],"summary":"Approve Access Request","description":"Approve a pending request (tier 2), for at most the time asked for.\n\nAN OPERATOR CANNOT APPROVE THEIR OWN REQUEST. That is almost structural\nalready — this route resolves the tenant from the caller's credential, and\na platform-admin credential resolves to no tenant at all (409) — but\n\"almost\" is not a boundary. The case it does not cover is real: IG1 staff\nhold logins inside pilot tenants, so the same human can hold both a\nplatform credential and a member seat in the customer's project. The\nexplicit refusal below is what closes it, and it is checked against the\ngrant's recorded operator_user_id rather than against a role.","operationId":"approve_access_request","security":[{"HTTPBearer":[]}],"parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","title":"Grant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/access/requests/{grant_id}/deny":{"post":{"tags":["operator-access"],"summary":"Deny Access Request","description":"Refuse a pending request (tier 2).\n\nNo transparency event is published, and that is a decision rather than an\nomission: the events schema's action vocabulary\n(schemas.OperatorAccessPayload) covers the life of a GRANT —\nrequested/activated/used/expired/revoked — and a refusal never became one.\nInventing a sixth action would 422 at publish under ``extra=\"forbid\"`` and\ndrop the record silently, which is the worst of both. The refusal is in\nthe store, which is what BOTH the customer's list and the operator's own\nlist read.","operationId":"deny_access_request","security":[{"HTTPBearer":[]}],"parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","title":"Grant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/access/requests/{grant_id}/revoke":{"post":{"tags":["operator-access"],"summary":"Revoke Access Request","description":"Withdraw consent — IMMEDIATE AND UNCONDITIONAL (tier 2).\n\nUnconditional means what it says: a standing-origin grant and a\nbreak-glass grant are revoked here exactly like an approved one. There is\nno state in which the platform gets to answer \"you cannot revoke this\",\nbecause a right to withdraw that takes a support ticket is not a right,\nand one that has exceptions is an exception list an incident will be\nargued into.\n\nAlready-revoked is a 200, not a 409: the customer's intent is satisfied,\nand making them parse an error to learn that is noise at the exact moment\nthey are least inclined to forgive it.","operationId":"revoke_access_request","security":[{"HTTPBearer":[]}],"parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","title":"Grant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/access/log":{"get":{"tags":["operator-access"],"summary":"Get Access Log","description":"This project's access-transparency history, newest first (tier 0).\n\nTHREE SOURCES, merged, because no one of them can answer the question:\n\n  * the STORE — every grant's life reconstructed from its own timestamps.\n    Durable and complete for requested/activated/expired/revoked, and the\n    only source that still exists next quarter;\n  * the BUS — GET /v1/events?topic=iam.operator.access. The events service\n    stamps this topic with the tenant (schemas.TOPIC_TENANT_FIELDS), so\n    the customer reads their own rows WITHOUT an admin role, which is the\n    entire reason the stamp is there. It is also the only source of\n    ``grant.used`` — what was actually touched under a grant, which no\n    grant record can reconstruct — and it is served from an in-memory ring\n    buffer, so it is the recent half of the answer, not the archive;\n  * this pod's RING — what we published (or failed to publish) recently.\n\nDeduplicated on (grant_id, action) keeping the EARLIEST timestamp, so a\nstore-reconstructed row and its bus twin collapse into one, and\n``sources`` reports how many rows each half contributed. That count is not\ndecoration: it is what makes a gate over this endpoint unable to pass\nwhile blind — a bus that has gone silent shows as ``bus: 0`` against a\nnon-zero store, rather than as a shorter list nobody notices (OPERATIONS\ngotcha 207).","operationId":"get_access_log","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":0,"events":[],"sources":{"bus":0,"bus_reachable":true,"ring":0,"store":0}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones":{"get":{"tags":["dns"],"summary":"List Zones","description":"The caller's own DNS zones.","operationId":"list_dns_zones","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":1,"zones":[{"created_at":"2026-08-22T17:36:38.000000","description":"","email":"user-04@example.com","id":"23337776-31e2-4548-8eba-f7f0925b9155","name":"Example Organisation","serial":1787422266,"status":"ACTIVE","ttl":3600,"updated_at":"2026-08-22T18:11:13.000000"}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dns"],"summary":"Create Zone","description":"Create a zone.\n\nTwo refusals happen HERE rather than at Designate, because Designate cannot\nknow about either: the platform's own namespace (a tenant zone under\ncloud.ig1.com would take over names other customers resolve) and the\nper-tenant ceiling.","operationId":"create_dns_zone","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"count":1,"zones":[{"created_at":"2026-08-22T17:36:38.000000","description":"","email":"user-04@example.com","id":"23337776-31e2-4548-8eba-f7f0925b9155","name":"Example Organisation","serial":1787422266,"status":"ACTIVE","ttl":3600,"updated_at":"2026-08-22T18:11:13.000000"}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones/{zone_id}":{"get":{"tags":["dns"],"summary":"Get Zone","operationId":"get_dns_zone","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dns"],"summary":"Delete Zone","description":"Delete a zone and every record in it (tier 2 — it is not recoverable).","operationId":"delete_dns_zone","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones/{zone_id}/recordsets":{"get":{"tags":["dns"],"summary":"List Recordsets","operationId":"list_dns_recordsets","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["dns"],"summary":"Create Recordset","description":"Create a recordset.\n\nThe name is accepted short ('www') and qualified against the zone here, so\n'www', 'www.example.com' and 'www.example.com.' cannot become three\ndifferent records — see dns.qualify.","operationId":"create_dns_recordset","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordSetCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones/{zone_id}/recordsets/{recordset_id}":{"patch":{"tags":["dns"],"summary":"Update Recordset","operationId":"update_dns_recordset","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}},{"name":"recordset_id","in":"path","required":true,"schema":{"type":"string","title":"Recordset Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordSetUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["dns"],"summary":"Delete Recordset","operationId":"delete_dns_recordset","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}},{"name":"recordset_id","in":"path","required":true,"schema":{"type":"string","title":"Recordset Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones/{zone_id}/delegation":{"get":{"tags":["dns"],"summary":"Get Delegation","description":"Where this domain currently points, and where it should.\n\nTHE NAMESERVERS ARE READ FROM THE ZONE, never from a constant here. They\ncome from the pool that serves it, they are already in the zone's own apex\nNS recordset, and the day the platform serves a second nameserver every\nzone gains it there. A list compiled in this file would keep telling\ncustomers to publish one nameserver for weeks after that.","operationId":"get_dns_delegation","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dns/zones/{zone_id}/import":{"post":{"tags":["dns"],"summary":"Import Records","description":"Create many recordsets from a zone file or a pasted list.\n\nDRY RUN IS THE DEFAULT (dns.ImportRequest.dry_run). This writes in bulk to\nthe thing that decides where a customer's traffic goes, and the plan it\nreturns is the same shape either way — so the console previews with the\nidentical call it will later commit, rather than a second code path that\ncan disagree with the one that runs.\n\nPARTIAL APPLICATION IS REPORTED, NOT HIDDEN. Designate takes one recordset\nper call, so an import of thirty records is thirty writes and the tenth can\nfail. The response lists the outcome of every entry INCLUDING the ones\nnever attempted after a ceiling was hit, because the customer's next action\n— re-run, or fix one line — depends on knowing which half landed. A\nre-run is safe: an entry already present with the same values comes back\nas \"identical\" rather than as an error.","operationId":"import_dns_records","security":[{"HTTPBearer":[]}],"parameters":[{"name":"zone_id","in":"path","required":true,"schema":{"type":"string","title":"Zone Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"tags":["root"],"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"docs":"/docs","plane":"customer","routers":["account","alarms","audit","credentials","databases","dns","edge","health","metrics","object-storage","openstack","operatoraccess-customer","quotas","registry","signups","status","w7-proxy"],"service":"ig1-cloud-api","version":"0.21.2"}}}}}}},"/v1/whoami":{"get":{"tags":["auth"],"summary":"Whoami","description":"Echo the caller's resolved identity — the portal's view-gating source\n(phase 19B): user_id, Zitadel project roles, and the per-caller tenant\nresolved in phase 20 (id, display name, resolution source, and whether\nthe caller sees all projects). Phase 26 adds the credential tier and\nthe originating dynamic credential id (when any).\n\nWHO THE TOKEN BELONGS TO, IN WORDS (2026-08-23). `user_name` and\n`user_email` come from the same introspection response the gate already\nreads, and `account` carries the number, alias and company name. This\nis the ONE call every client resolves identity through — the CLI's\n`ig1 whoami`, the SDKs' AuthApi, the MCP's `whoami` tool and the\nportal's navigation — so a caller holding nothing but a bearer token\ncan answer \"who am I, which account, which project\" before they build\nanything in the wrong one. Both name fields may be empty (a machine\nuser often asserts neither); they are display, never authorization.","operationId":"whoami_v1_whoami_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"account":{"account_number":"012345678901","account_number_display":"0123-4567-8901","alias":"example","name":"Example Organisation","project_id":"00000000000000000000000000000001","project_name":"ig1-customer-example-1"},"all_projects":false,"billing_access":true,"credential_id":null,"credential_label":"","plane":"customer","projects":{"available":[{"credential_id":"cred-000001","project_id":"00000000000000000000000000000001","project_name":"ig1-customer-example-1","tier":2},{"credential_id":"cred-000002","project_id":"00000000000000000000000000000002","project_name":"ig1-customer-example-2","tier":0}],"current":"00000000000000000000000000000001"},"role_label":null,"roles":["customer"],"tenant_id":"00000000000000000000000000000001","tenant_name":"ig1-customer-example-1","tenant_source":"tenant-map","tier":2,"user_email":"","user_id":"000000000000000002","user_name":""}}}}},"security":[{"HTTPBearer":[]}]}}},"components":{"schemas":{"AccountClose":{"properties":{"confirm":{"type":"string","title":"Confirm","description":"The account number, digits only, typed exactly."},"confirm_projects":{"type":"boolean","title":"Confirm Projects","description":"A SECOND, separate acknowledgement that every project in the account and everything inside them is destroyed. Two fields because they are two facts: people who intend to close an account do not always know it takes their projects with it."}},"type":"object","required":["confirm","confirm_projects"],"title":"AccountClose"},"AlarmCreate":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"metric":{"type":"string","enum":["cpu_percent","memory_rss_kb","memory_actual_kb"],"title":"Metric"},"resource_id":{"type":"string","maxLength":200,"minLength":1,"title":"Resource Id"},"comparison":{"type":"string","enum":["gt","gte","lt","lte"],"title":"Comparison"},"threshold":{"type":"number","title":"Threshold"},"window_minutes":{"type":"integer","maximum":1440.0,"minimum":1.0,"title":"Window Minutes","default":5},"enabled":{"type":"boolean","title":"Enabled","default":true}},"type":"object","required":["name","metric","resource_id","comparison","threshold"],"title":"AlarmCreate","description":"The create body.\n\nEvery field a client may set is here, and the ones it may NOT — id,\nproject_id, state, the timestamps — are absent by construction rather than\nignored later. A client-settable ``state`` would let a caller declare an\nalarm ``ok`` without anything having measured it, which is precisely the\nlie ig1_api/alarms.py is built to make impossible."},"AliasUpdate":{"properties":{"alias":{"type":"string","maxLength":63,"minLength":3,"title":"Alias"}},"type":"object","required":["alias"],"title":"AliasUpdate"},"ApproveBody":{"properties":{"seconds":{"anyOf":[{"type":"integer","minimum":60.0},{"type":"null"}],"title":"Seconds"}},"additionalProperties":false,"type":"object","title":"ApproveBody","description":"Optionally a SHORTER life than the operator asked for.\n\nNo upper bound in the schema on purpose: operatoraccess.cap_seconds\nclamps to APPROVED_MAX_SECONDS, so asking for a year yields the ceiling\nrather than a 422 — the customer is always allowed to ask for less, and\nthe platform's ceiling is the platform's business to enforce, not\nsomething the customer should have to know to fill in a form."},"ApproveRequest":{"properties":{"tier":{"type":"string","title":"Tier","default":"discovery"},"tenant_name":{"anyOf":[{"type":"string","maxLength":64,"minLength":2},{"type":"null"}],"title":"Tenant Name"}},"type":"object","title":"ApproveRequest"},"BackupSpec":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"retention_days":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Retention Days","default":7},"schedule":{"type":"string","maxLength":40,"title":"Schedule","default":"0 3 * * *"}},"type":"object","title":"BackupSpec","description":"The backup block (phase 52) — CNPG's barman-cloud plugin does the\nwork; these are the only knobs a tenant gets. ``enabled`` exists so the\nblock's presence and its meaning cannot drift apart (``{\"enabled\":\nfalse}`` and omitting the block are deliberately the same thing)."},"BillingGrant":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"BillingGrant"},"BucketCreate":{"properties":{"name":{"type":"string","maxLength":63,"minLength":3,"title":"Name"}},"type":"object","required":["name"],"title":"BucketCreate","description":"The create body — name only; the owner is the resolved tenant."},"CredentialCreate":{"properties":{"kind":{"type":"string","pattern":"^(api-key|mcp)$","title":"Kind"},"tier":{"type":"integer","maximum":2.0,"minimum":0.0,"title":"Tier"},"label":{"type":"string","maxLength":120,"minLength":1,"title":"Label"},"expires_in_days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days"}},"type":"object","required":["kind","tier","label"],"title":"CredentialCreate"},"DatabaseCreate":{"properties":{"engine":{"type":"string","pattern":"^(postgres|kafka)$","title":"Engine"},"name":{"type":"string","maxLength":50,"minLength":1,"title":"Name"},"size_gb":{"type":"integer","maximum":250.0,"minimum":1.0,"title":"Size Gb","description":"Storage per replica, in GB (max 250). ``size_gb x replicas`` must also be at most 300 GB — a platform ceiling derived from measured Ceph and Cinder capacity that no tier lifts, so 250 GB is reachable at one replica but not at three. Refused at create with the arithmetic, never half-applied."},"version":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Version"},"replicas":{"type":"integer","maximum":3.0,"minimum":1.0,"title":"Replicas","description":"PostgreSQL instance count (1-3). It MULTIPLIES storage: every replica is its own PVC, so the 300 GB footprint ceiling is what bounds this against size, not the replica count on its own.","default":1},"backup":{"anyOf":[{"$ref":"#/components/schemas/BackupSpec"},{"type":"null"}]}},"type":"object","required":["engine","name","size_gb"],"title":"DatabaseCreate","description":"The create body — engine/name/size, plus the phase-52 durability\nknobs: replica count and the backup block. The tenant (and therefore\nthe namespace) is the resolved credential's, never caller-supplied."},"DatabaseResize":{"properties":{"size_gb":{"anyOf":[{"type":"integer","maximum":250.0,"minimum":1.0},{"type":"null"}],"title":"Size Gb","description":"Target storage per replica in GB. Must be LARGER than the current size. The resulting ``size_gb x replicas`` must be at most 300 GB — the same platform footprint ceiling the create path applies, evaluated on the FINAL shape when both fields move together."},"replicas":{"anyOf":[{"type":"integer","maximum":3.0,"minimum":1.0},{"type":"null"}],"title":"Replicas","description":"Target PostgreSQL instance count (1-3), changed in place. Adding a replica adds a full PVC, so a replica change alone can cross the 300 GB footprint ceiling."}},"type":"object","title":"DatabaseResize","description":"The resize body — storage and/or replica count (phase 52).\n\n``size_gb`` is the TARGET, not a delta, and it is bounded by the same\nMAX_SIZE_GB the create schema uses so the two cannot drift apart — and by\nthe same MAX_FOOTPRINT_GB cap on ``size_gb x replicas``, which is the\nbound the phase-52 multiplication actually needs (gotcha 397). The\ngrow-only rule is not expressible here (it needs the instance's current\nsize), so it lives in databases.refuse_shrink where the current size is\nknown.\n\n``replicas`` is PostgreSQL-only and changes IN PLACE — CNPG adds or\nremoves instances live, so the provider must never replace the cluster\n(and its data) for a replica change. At least one of the two fields is\nrequired; a no-op value is refused in the handler, where the current\nshape is known."},"DatabaseRestore":{"properties":{"name":{"type":"string","maxLength":50,"minLength":1,"title":"Name"},"to_point_in_time":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"To Point In Time"}},"type":"object","required":["name"],"title":"DatabaseRestore","description":"The restore body (phase 58's second half): restore a backup-enabled\ninstance into a NEW cluster, never in place.\n\n``name`` is the NEW instance's name — the source is read from the path.\n``to_point_in_time`` (RFC 3339) maps to CNPG's recoveryTarget.targetTime;\nabsent, recovery replays to the latest archived WAL — \"the moment of the\ndisaster\" for anything archived since the last base backup. The new\ninstance inherits the source's size and replica count and gets its own\narchive identity (its own ScheduledBackup on the house schedule)."},"DenyRule":{"properties":{"methods":{"items":{"type":"string"},"type":"array","title":"Methods"},"paths":{"items":{"type":"string"},"type":"array","title":"Paths"},"reason":{"type":"string","title":"Reason","default":""}},"type":"object","title":"DenyRule","description":"One explicit deny. Empty list means \"every value\" for that dimension.\n\nA rule with BOTH lists empty would deny every request in the account, which\nis a lockout somebody writes by accident rather than on purpose, so\n`validate_policy` refuses it at the door instead of enforcing it."},"DomainClaim":{"properties":{"custom_domain":{"type":"string","maxLength":253,"minLength":1,"title":"Custom Domain"}},"type":"object","required":["custom_domain"],"title":"DomainClaim","description":"The body of a claim. One field, because everything else about a domain\n— the token, the state, the instructions — is ours to derive, and a\nclient-settable verification stamp would be the whole check."},"ExposureCreate":{"properties":{"name":{"type":"string","pattern":"^[a-z0-9]([a-z0-9-]{0,28}[a-z0-9])?$","title":"Name","description":"DNS label; the public hostname becomes {name}-{tenant-slug}.10.57.8.75.nip.io"},"target_ip":{"type":"string","maxLength":64,"minLength":1,"title":"Target Ip"},"target_port":{"type":"integer","maximum":65535.0,"minimum":1.0,"title":"Target Port"},"protocol":{"type":"string","const":"https","title":"Protocol","default":"https"},"custom_domain":{"type":"string","maxLength":253,"title":"Custom Domain","description":"Optional: a domain you own (app.example.com). It is CLAIMED here and routed only after POST /v1/edge/exposures/{id}/verify confirms the TXT challenge. The derived hostname above always works.","default":""}},"type":"object","required":["name","target_ip","target_port"],"title":"ExposureCreate","description":"The create body. The fields a client may NOT set — id, hostname,\nproject_id, status, generation — are absent by construction rather than\nignored later: a client-settable hostname or owner is a routing key the\ncaller could point at somebody else."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImportRequest":{"properties":{"zonefile":{"type":"string","maxLength":262144,"minLength":1,"title":"Zonefile"},"dry_run":{"type":"boolean","title":"Dry Run","default":true},"on_conflict":{"type":"string","enum":["skip","replace"],"title":"On Conflict","default":"skip"}},"additionalProperties":false,"type":"object","required":["zonefile"],"title":"ImportRequest","description":"A paste, and what to do about records that already exist.\n\n`dry_run` defaults to TRUE. An import is a bulk write to the thing that\ndecides where a customer's traffic goes, and the console shows the plan\nbefore anything is created — so the safe value is the default, and the\ncaller writing a script has to say the word."},"IncidentCreate":{"properties":{"kind":{"type":"string","pattern":"^(incident|maintenance)$","title":"Kind"},"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"detail":{"type":"string","maxLength":2000,"title":"Detail","default":""},"status":{"type":"string","pattern":"^(open|scheduled|in_progress|resolved)$","title":"Status","default":"open"},"started_at":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Started At"}},"type":"object","required":["kind","title"],"title":"IncidentCreate","description":"The declare body — kind/title plus optional detail/status/start."},"OuCreate":{"properties":{"path":{"type":"string","maxLength":128,"minLength":1,"title":"Path","description":"Dotted OU path, e.g. `prod` or `prod.eu-west`. The path IS the parentage — creating `prod.eu-west` requires `prod` to exist."},"name":{"type":"string","maxLength":128,"title":"Name","description":"Display name.","default":""}},"type":"object","required":["path"],"title":"OuCreate"},"PolicyCreate":{"properties":{"name":{"type":"string","maxLength":128,"title":"Name","default":""},"target":{"type":"string","title":"Target","description":"OU path this applies to; empty = the account root, i.e. every project.","default":""},"max_tier":{"anyOf":[{"type":"integer","maximum":2.0,"minimum":0.0},{"type":"null"}],"title":"Max Tier","description":"Ceiling on the effective tier (0 read-only, 1 operate, 2 destroy). Composes by MINIMUM down the tree."},"deny":{"items":{"$ref":"#/components/schemas/DenyRule"},"type":"array","title":"Deny"}},"type":"object","title":"PolicyCreate"},"PolicyUpdate":{"properties":{"mode":{"type":"string","enum":["approval_required","standing","denied"],"title":"Mode"},"scope":{"type":"string","enum":["read","operate"],"title":"Scope","default":"read"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"note":{"type":"string","maxLength":500,"title":"Note","default":""}},"additionalProperties":false,"type":"object","required":["mode"],"title":"PolicyUpdate","description":"The PUT body — and note what is NOT in it.\n\n``tenant_id``, ``set_by`` and ``set_at`` are absent by construction, not\nstripped later: with ``extra=\"forbid\"`` a client that tries to set them\ngets a 422. Consent given by nobody in particular is not consent, so the\nserver stamps who set it from the CALLER; and a body-supplied tenant is\nnot a boundary (the phase-20 rule)."},"ProjectCreate":{"properties":{"name":{"type":"string","maxLength":32,"minLength":1,"title":"Name","description":"A short name for the project, e.g. `staging`. Slugified into the Keystone project name. It only has to be unique within YOUR account — two accounts may both have a `staging`."}},"type":"object","required":["name"],"title":"ProjectCreate"},"ProjectDelete":{"properties":{"confirm":{"type":"string","title":"Confirm","description":"The project's own name, typed exactly. A destructive verb whose confirmation is a boolean is a verb that fires on a mis-click; this one cannot be issued without having read the name first."}},"type":"object","required":["confirm"],"title":"ProjectDelete"},"RecordSetCreate":{"properties":{"name":{"type":"string","maxLength":254,"minLength":1,"title":"Name"},"type":{"type":"string","enum":["A","AAAA","CNAME","MX","TXT","SRV","NS","PTR","CAA","SPF","SSHFP"],"title":"Type"},"records":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Records"},"ttl":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":60.0},{"type":"null"}],"title":"Ttl"},"description":{"type":"string","title":"Description","default":""}},"additionalProperties":false,"type":"object","required":["name","type","records"],"title":"RecordSetCreate"},"RecordSetUpdate":{"properties":{"records":{"anyOf":[{"items":{"type":"string"},"type":"array","minItems":1},{"type":"null"}],"title":"Records"},"ttl":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":60.0},{"type":"null"}],"title":"Ttl"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"additionalProperties":false,"type":"object","title":"RecordSetUpdate"},"RejectRequest":{"properties":{"reason":{"type":"string","maxLength":500,"title":"Reason","default":""}},"type":"object","title":"RejectRequest"},"SignupRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email"},"org_name":{"type":"string","maxLength":64,"minLength":2,"title":"Org Name"}},"type":"object","required":["email","org_name"],"title":"SignupRequest"},"TokenExchangeRequest":{"properties":{"subject_token":{"type":"string","minLength":1,"title":"Subject Token","description":"The k8s service-account JWT"},"subject_token_type":{"type":"string","pattern":"^urn:ietf:params:oauth:token-type:jwt$","title":"Subject Token Type","default":"urn:ietf:params:oauth:token-type:jwt"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope","description":"Optional project id override; defaults to the issuer's mapped tenant"}},"type":"object","required":["subject_token"],"title":"TokenExchangeRequest","description":"RFC 8693 style token-exchange request."},"TokenExchangeResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"Bearer"},"expires_in":{"type":"integer","title":"Expires In"}},"type":"object","required":["access_token","expires_in"],"title":"TokenExchangeResponse"},"UserInvite":{"properties":{"email":{"type":"string","maxLength":320,"minLength":3,"title":"Email"},"display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Display Name"},"role":{"type":"string","pattern":"^(admin|customer|customer-viewer|customer-engineer|customer-admin)$","title":"Role"},"project_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Project Id"}},"type":"object","required":["email","role"],"title":"UserInvite"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VersioningUpdate":{"properties":{"status":{"type":"string","pattern":"^(enabled|suspended)$","title":"Status"}},"type":"object","required":["status"],"title":"VersioningUpdate","description":"The versioning body. ``disabled`` is deliberately not accepted — S3\nhas no such transition (see objectstorage.VERSIONING_UNSET)."},"ZoneCreate":{"properties":{"name":{"type":"string","maxLength":254,"minLength":3,"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"description":{"type":"string","title":"Description","default":""},"ttl":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":60.0},{"type":"null"}],"title":"Ttl"}},"additionalProperties":false,"type":"object","required":["name"],"title":"ZoneCreate"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}