# JSON representation

Bodies are boring on purpose, and the two rules worth internalising are these: **every
name is `snake_case`**, everywhere — path segments, fields, query parameters, enum values,
custom-method verbs — and **an unset field is absent, never `null`**. A response you can
predict is a response you can parse once.

The other half of this page is what every resource carries whatever it is: its `name` and
`id`, when it was made and last changed, and the `etag` that makes a safe write possible.
Each field also declares its *behavior* — required, output-only, immutable — and that
declaration is in the reference, on the field, not in a paragraph somebody has to
remember to update.

## 11. JSON representation

- **Naming.** `snake_case` for every field, query parameter and enum value
  (D1). No abbreviations that are not already in the domain vocabulary.
- **Absent vs null.** In a response, an unset optional field is **omitted**;
  `null` is never emitted. In a request, `null` means "clear this field" and is
  only honoured together with `update_mask` ([§7](/concepts/methods)).
- **Booleans** are `true`/`false`. Never `1`/`0`, never `"true"`.
- **Numbers** are JSON numbers. An `int64` that can exceed 2^53 is a string —
  none exist today, and if one appears it MUST be documented.
- **Enums** are `UPPER_SNAKE_CASE` strings, never integers. Every enum reserves
  a zero-value `<NAME>_UNSPECIFIED` and clients MUST tolerate an unknown value
  rather than failing (AIP-126) — that is what lets us add one without a
  version bump.
- **Timestamps are RFC 3339 UTC strings**: `"2026-08-24T10:15:30Z"`. Not epoch
  milliseconds.
- **Durations** are strings with a unit suffix (`"3.5s"`), per AIP-142.
- **Dates without a time** are `"2026-08-24"`.
- **Money** takes the `google.type.Money` shape —
  `{ "currency_code": "BRL", "units": "12", "nanos": 500000000 }`. Never a float,
  on the wire or behind it: the AI platform's balances and charges are int64
  nanos of a currency all the way down, and the one place a fraction of a cent
  would be rounded away is named where it happens.
- **Empty arrays are emitted**, empty objects are omitted. `visible_view_ids: []`
  is meaningful (it means "all views") and must survive a round trip.

## 12. Standard fields

Every resource MUST carry:

| Field | Behavior | Notes |
|---|---|---|
| `name` | OUTPUT_ONLY | Full resource name ([§5](/concepts/resources)). |
| `id` | OUTPUT_ONLY | Final segment of `name` (D3). Absent on a singleton, which has no such segment ([§9](/concepts/methods)). |
| `create_time` | OUTPUT_ONLY | RFC 3339. Renamed from `created_at`. |
| `update_time` | OUTPUT_ONLY | RFC 3339. Renamed from `updated_at`. |
| `etag` | OUTPUT_ONLY | Required on any resource with concurrent editors ([§14](/concepts/concurrency)). |

And SHOULD use these names when the concept applies (AIP-148): `display_name`,
`title`, `description`, `parent`, `delete_time`, `expire_time`, `uid`,
`labels`, `annotations`. A resource MUST NOT invent a synonym for one of them.

### Field behavior (AIP-203)

Every field is exactly one of:

- **REQUIRED** — Create fails `INVALID_ARGUMENT` without it.
- **OPTIONAL** — the default.
- **OUTPUT_ONLY** — server-set; ignored if sent (D5).
- **IMMUTABLE** — settable on Create, rejected with `INVALID_ARGUMENT` on
  Update.
- **INPUT_ONLY** — accepted, never echoed (passwords, one-time tokens).

The behavior is part of the contract and MUST appear in the generated OpenAPI
document. It is not a comment.
