PlanyDocs
API reference

AuditService

api.plany.cloud:443Global4 methods

Read the record of who did what on your account. Every call that changes something — on any product, in any region — is recorded once, with who made it, from where, and whether it was allowed. The trail is append-only and has no write or delete method, and VerifyIntegrity proves that no entry has been altered or removed.

ListAuditEvents

Read
plany.audit.v1.AuditService/ListAuditEvents

Lists audit events for an account, newest first.

Parameters

  • parentstringRequired

    accounts/{account}

  • page_sizeint32
  • page_tokenstring

    Opaque. Keyset over (event_time, sequence) rather than an offset: an audit table is append-heavy and deep, and an offset scan degrades linearly with page depth exactly when somebody is paging back through an incident.

  • Show child attributes
    • projectstring

      Restrict to one project.

    • resourcestring

      Restrict to one resource, exactly. The "everything that happened to this game server" query.

    • principalstring

      Restrict to one actor.

    • credential_idstring

      Restrict to one credential. What a leaked API key is investigated with.

    • servicestring

      Restrict to one service, e.g. playpute.

    • methodstring

      Restrict to one full method.

    • locationstring

      Restrict to one region.

    • time_rangeInterval

      Window, by event time. Half-open: start inclusive, end exclusive.

    • failures_onlybool

      Only calls that failed.

      The security view. A run of denials from one credential is the clearest available signal that somebody is trying doors.

Returns

ListAuditEventsResponse.

grpcurl -H "authorization: Bearer $PLANY_API_KEY" \
-d '{
"parent": "accounts/",
"page_size": 50
}' \
api.plany.cloud:443 plany.audit.v1.AuditService/ListAuditEvents
Example response
{
"audit_events": [
{
"name": "accounts//auditEvents/my-audit-event",
"method": "",
"service": "",
"resource": "",
"account": "accounts/",
"project": "projects/",
"location": "",
"actor": {
"principal": "",
"principal_type": "",
"email": "",
"ip_address": "",
"user_agent": "",
"credential_id": "my-credential"
},
"status": {},
"request_json": "",
"operation": "",
"trace_id": "my-trace",
"event_time": "2026-10-04T12:00:00Z",
"duration_micros": "0",
"sequence": "0",
"hash": "",
"previous_hash": "",
"mutating": false
}
],
"next_page_token": ""
}

GetAuditEvent

Read
plany.audit.v1.AuditService/GetAuditEvent

Parameters

  • namestringRequired

Returns

AuditEvent.

grpcurl -H "authorization: Bearer $PLANY_API_KEY" \
-d '{
"name": "accounts//auditEvents/my-audit-event"
}' \
api.plany.cloud:443 plany.audit.v1.AuditService/GetAuditEvent
Example response
{
"name": "accounts//auditEvents/my-audit-event",
"method": "",
"service": "",
"resource": "",
"account": "accounts/",
"project": "projects/",
"location": "",
"actor": {
"principal": "",
"principal_type": "",
"email": "",
"ip_address": "",
"user_agent": "",
"credential_id": "my-credential"
},
"status": {},
"request_json": "",
"operation": "",
"trace_id": "my-trace",
"event_time": "2026-10-04T12:00:00Z",
"duration_micros": "0",
"sequence": "0",
"hash": "",
"previous_hash": "",
"mutating": false
}

VerifyIntegrity

plany.audit.v1.AuditService/VerifyIntegrity

Verifies that an account's chain has not been tampered with.

The RPC that makes the trail worth having in a dispute. It recomputes every hash over a window and checks each links to the one before, so "nothing was altered or removed" is something the platform can demonstrate rather than assert.

Parameters

  • parentstringRequired
  • time_rangeInterval

    Window to verify. Defaults to the whole chain, which is the honest default — verifying a window somebody chose proves less than verifying all of it.

Returns

VerifyIntegrityResponse.

grpcurl -H "authorization: Bearer $PLANY_API_KEY" \
-d '{
"parent": ""
}' \
api.plany.cloud:443 plany.audit.v1.AuditService/VerifyIntegrity
Example response
{
"intact": false,
"events_checked": "0",
"breaks": [
{
"audit_event": "",
"sequence": "0",
"kind": "KIND_CONTENT_ALTERED",
"detail": ""
}
],
"verified_range": {
"start_time": "2026-10-01T00:00:00Z",
"end_time": "2026-10-04T00:00:00Z"
}
}

SummariseActivity

Read
plany.audit.v1.AuditService/SummariseActivity

Summarises an account's activity over a period: counts by kind of change, as shown on the console's overview.

Counted over every entry in the period, not only the first page, so the totals are exact.

Parameters

Returns

SummariseActivityResponse.

grpcurl -H "authorization: Bearer $PLANY_API_KEY" \
-d '{
"parent": ""
}' \
api.plany.cloud:443 plany.audit.v1.AuditService/SummariseActivity
Example response
{
"total_events": "0",
"failed_events": "0",
"by_principal": [
{
"key": "",
"events": "0"
}
],
"by_service": [
{
"key": "",
"events": "0"
}
],
"by_method": [
{
"key": "",
"events": "0"
}
],
"daily": [
{
"start_time": "2026-10-04T12:00:00Z",
"events": "0",
"failed": "0"
}
],
"time_range": {
"start_time": "2026-10-01T00:00:00Z",
"end_time": "2026-10-04T00:00:00Z"
}
}

The AuditEvent.Actor object

Who made the call.

Attributes

  • principalstring

    Principal name, e.g. users/u_123 or serviceAccounts/sa_456.

  • principal_typestring

    PrincipalType as a string: USER, SERVICE_ACCOUNT, API_KEY, SERVICE.

  • emailstring

    Email at the time of the call, denormalised on purpose.

    A user can be renamed or removed, and an audit trail that resolved identities at read time would quietly rewrite history — the entry would stop naming the person who actually did it.

  • ip_addressstring

    Where the call came from, as Plany's API saw it.

  • user_agentstring
  • credential_idstring

    Which credential was used.

    The field that turns "an API key was leaked" from a panic into a query: everything that key ever did, in one filter.

AuditEvent.Actor
{
"principal": "",
"principal_type": "",
"email": "",
"ip_address": "",
"user_agent": "",
"credential_id": "my-credential"
}

The AuditFilter object

AuditFilter narrows a listing.

Structured fields rather than a query language. Every one of these is an indexed column, so a filter cannot express a question the store would have to answer with a full scan — which is what an expression language would allow, and what would make one customer's clever query everybody's outage.

Attributes

  • projectstring

    Restrict to one project.

  • resourcestring

    Restrict to one resource, exactly. The "everything that happened to this game server" query.

  • principalstring

    Restrict to one actor.

  • credential_idstring

    Restrict to one credential. What a leaked API key is investigated with.

  • servicestring

    Restrict to one service, e.g. playpute.

  • methodstring

    Restrict to one full method.

  • locationstring

    Restrict to one region.

  • time_rangeInterval

    Window, by event time. Half-open: start inclusive, end exclusive.

  • failures_onlybool

    Only calls that failed.

    The security view. A run of denials from one credential is the clearest available signal that somebody is trying doors.

AuditFilter
{
"project": "projects/",
"resource": "",
"principal": "",
"credential_id": "my-credential",
"service": "",
"method": "",
"location": "",
"time_range": {
"start_time": "2026-10-01T00:00:00Z",
"end_time": "2026-10-04T00:00:00Z"
},
"failures_only": false
}

The SummariseActivityResponse.Bucket object

Events per day across the window, oldest first. What the console draws.

Attributes

SummariseActivityResponse.Bucket
{
"start_time": "2026-10-04T12:00:00Z",
"events": "0",
"failed": "0"
}

The SummariseActivityResponse.Count object

One row of a breakdown.

Attributes

  • keystring
  • eventsint64
SummariseActivityResponse.Count
{
"key": "",
"events": "0"
}

The VerifyIntegrityResponse.Break object

Where the chain first broke, if it did.

Attributes

  • audit_eventstring

    Entry whose hash or link did not check out.

  • sequenceint64
  • KIND_CONTENT_ALTEREDKIND_CHAIN_BROKENKIND_SEQUENCE_GAP
  • detailstring
VerifyIntegrityResponse.Break
{
"audit_event": "",
"sequence": "0",
"kind": "KIND_CONTENT_ALTERED",
"detail": ""
}

AuditEvent

AuditEvent is one thing somebody did to an account.

Every call that changes state on the platform produces exactly one, whichever product it was made to. It is not a log line: it answers "who deleted the production world save, from where, and were they allowed to" months later, which is why the trail is append-only and chained so that altering any entry is detectable.

  • namestringIdentifier

    accounts/{account}/auditEvents/{audit_event}

  • methodstringOutput only

    Full gRPC method, e.g. /plany.playpute.v1.PlayputeService/DeleteGameServer.

  • servicestringOutput only

    Service that handled it, e.g. playpute.

  • resourcestringOutput only

    Resource acted on, as a resource name.

    Extracted from the request's name or parent field by the interceptor, so it is populated without any handler cooperating. This is what makes "everything that ever happened to this game server" a filter rather than a text search.

  • accountstringOutput only
  • projectstringOutput only
  • locationstringOutput only

    Region the call was served in. Empty for the global plane.

  • actorAuditEvent.ActorOutput only
    Show child attributes
    • principalstring

      Principal name, e.g. users/u_123 or serviceAccounts/sa_456.

    • principal_typestring

      PrincipalType as a string: USER, SERVICE_ACCOUNT, API_KEY, SERVICE.

    • emailstring

      Email at the time of the call, denormalised on purpose.

      A user can be renamed or removed, and an audit trail that resolved identities at read time would quietly rewrite history — the entry would stop naming the person who actually did it.

    • ip_addressstring

      Where the call came from, as Plany's API saw it.

    • user_agentstring
    • credential_idstring

      Which credential was used.

      The field that turns "an API key was leaked" from a panic into a query: everything that key ever did, in one filter.

  • statusStatusOutput only

    Outcome. OK means it succeeded.

    Failures are recorded too, and that is not padding: a run of PERMISSION_DENIED from one credential is the clearest signal available that somebody is trying doors, and a trail that only kept successes would show nothing at all until one opened.

  • request_jsonstringOutput only

    The request, as JSON, with secrets removed.

    Redaction is by field name at the interceptor — password, secret, token, key, credential — and the payload is capped. Storing a request verbatim would put customer passwords and API keys into the one table designed never to be deleted.

  • operationstringOutput only

    Long-running operation the call started, if any. Lets a slow create be followed from the audit entry to its outcome.

  • trace_idstringOutput only

    Trace id, so an entry links to the full distributed trace.

  • event_timeTimestampOutput only
  • duration_microsint64Output only

    How long the call took.

  • sequenceint64Output only

    Position in this account's chain, starting at 1.

    Monotonic and gapless. A missing number is the only evidence a deletion leaves, so it is the first thing integrity verification looks at.

  • hashstringOutput only

    SHA-256 over this entry's content and the previous entry's hash.

    The chain is what makes the trail tamper-evident rather than merely append-only. Editing one entry changes its hash, which breaks every entry after it — so altering history means rewriting all of it, and removing an entry leaves a gap that cannot be closed.

  • previous_hashstringOutput only

    The previous entry's hash. Empty for the first entry of an account.

  • mutatingboolOutput only

    Whether the call changed state. Only calls that change state are recorded, so this is always true.

ListAuditEventsResponse

  • Show child attributes
    • namestringIdentifier

      accounts/{account}/auditEvents/{audit_event}

    • methodstringOutput only

      Full gRPC method, e.g. /plany.playpute.v1.PlayputeService/DeleteGameServer.

    • servicestringOutput only

      Service that handled it, e.g. playpute.

    • resourcestringOutput only

      Resource acted on, as a resource name.

      Extracted from the request's name or parent field by the interceptor, so it is populated without any handler cooperating. This is what makes "everything that ever happened to this game server" a filter rather than a text search.

    • accountstringOutput only
    • projectstringOutput only
    • locationstringOutput only

      Region the call was served in. Empty for the global plane.

    • actorAuditEvent.ActorOutput only
      Show child attributes
      • principalstring

        Principal name, e.g. users/u_123 or serviceAccounts/sa_456.

      • principal_typestring

        PrincipalType as a string: USER, SERVICE_ACCOUNT, API_KEY, SERVICE.

      • emailstring

        Email at the time of the call, denormalised on purpose.

        A user can be renamed or removed, and an audit trail that resolved identities at read time would quietly rewrite history — the entry would stop naming the person who actually did it.

      • ip_addressstring

        Where the call came from, as Plany's API saw it.

      • user_agentstring
      • credential_idstring

        Which credential was used.

        The field that turns "an API key was leaked" from a panic into a query: everything that key ever did, in one filter.

    • statusStatusOutput only

      Outcome. OK means it succeeded.

      Failures are recorded too, and that is not padding: a run of PERMISSION_DENIED from one credential is the clearest signal available that somebody is trying doors, and a trail that only kept successes would show nothing at all until one opened.

    • request_jsonstringOutput only

      The request, as JSON, with secrets removed.

      Redaction is by field name at the interceptor — password, secret, token, key, credential — and the payload is capped. Storing a request verbatim would put customer passwords and API keys into the one table designed never to be deleted.

    • operationstringOutput only

      Long-running operation the call started, if any. Lets a slow create be followed from the audit entry to its outcome.

    • trace_idstringOutput only

      Trace id, so an entry links to the full distributed trace.

    • event_timeTimestampOutput only
    • duration_microsint64Output only

      How long the call took.

    • sequenceint64Output only

      Position in this account's chain, starting at 1.

      Monotonic and gapless. A missing number is the only evidence a deletion leaves, so it is the first thing integrity verification looks at.

    • hashstringOutput only

      SHA-256 over this entry's content and the previous entry's hash.

      The chain is what makes the trail tamper-evident rather than merely append-only. Editing one entry changes its hash, which breaks every entry after it — so altering history means rewriting all of it, and removing an entry leaves a gap that cannot be closed.

    • previous_hashstringOutput only

      The previous entry's hash. Empty for the first entry of an account.

    • mutatingboolOutput only

      Whether the call changed state. Only calls that change state are recorded, so this is always true.

  • next_page_tokenstring

SummariseActivityResponse

VerifyIntegrityResponse

  • intactbool

    Whether every entry checked hashes correctly and links to its predecessor.

  • events_checkedint64
  • Show child attributes
    • audit_eventstring

      Entry whose hash or link did not check out.

    • sequenceint64
    • KIND_CONTENT_ALTEREDKIND_CHAIN_BROKENKIND_SEQUENCE_GAP
    • detailstring
  • verified_rangeInterval

    The window actually verified.

Enums

VerifyIntegrityResponse.Break.Kind

ValueDescription
KIND_UNSPECIFIED
KIND_CONTENT_ALTEREDThe entry's content no longer hashes to its recorded hash: it was edited in place.
KIND_CHAIN_BROKENThe entry does not link to its predecessor's hash: something between them was removed or replaced.
KIND_SEQUENCE_GAPA sequence number is missing: an entry was deleted outright.