# Field formats

A model that guesses a field name gets either a `400` or, worse, a confident answer about a different chart. Here is the exact contract.

## A chart-shaped body

| Field | Type and format | Required |
|---|---|---|
| `date` | `YYYY-MM-DD`, and it must be a real calendar day | yes |
| `time` | `HH:mm:ss` | yes, or `timeUnknown: true` |
| `timezoneOffset` | number, hours from UTC, e.g. `5.75` | no, defaults to `0` (UTC) |
| `timezone` | IANA zone name such as `Europe/Kyiv`, or `auto` | no, and when sent it replaces `timezoneOffset` |
| `latitude` | decimal degrees, north positive | yes |
| `longitude` | decimal degrees, east positive | yes |
| `houseSystem` | a single letter, `P` by default | no |
| `city` | string, a label only | no |
| `zodiacType` | `tropical` or `sidereal` | no |

<Aside type="caution">
`city` geocodes nothing. It is a label for your own interface and it **never** stands in for coordinates.
</Aside>

## Short spellings are refused

`lat`, `lon`, `lng`, `long`, `tz`, `tzOffset`, `utcOffset`, `gmtOffset`, `timeZone` and `time_zone` return `400 INVALID_FIELD` naming the right field:

```json
{ "error": { "code": "INVALID_FIELD",
  "message": "Unsupported field \"tz\". Rename it to \"timezoneOffset\" (numeric hours from UTC, e.g. 5.75; a zone name goes in timezone)." } }
```

Case and separators carry no meaning: `TZ_Offset` and `tzoffset` are refused the same way. **Every** offending field is reported at once rather than the first one, so one round trip is enough to fix them all.

The strictness has a reason. Before this check, a field the API did not read silently meant an offset of `0`, and the answer was a confident chart for UTC. Three hours of offset is roughly 45° of ascendant, so a different rising sign, with no warning at all.

## `timezone`: a zone name instead of an offset

The offset has to be the one the clocks kept **on that date**, and it is easy to get wrong by hand: Kyiv in May 1990 kept Moscow summer time, UTC+4, not +3. Send `timezone` and the server works it out from the tz database, summer time included.

```json
{ "date": "1990-05-15", "time": "14:30:00", "timezone": "Europe/Kyiv",
  "latitude": 50.45, "longitude": 30.52 }
```

- `auto` looks the zone up from `latitude` and `longitude`. A name is more precise near a border.
- When `timezone` and `timezoneOffset` arrive together, `timezone` wins. `input.timezoneOffset` in the response shows the value that was used.
- A clock time that happened twice, when clocks went back, takes the first occurrence. A time that never happened, when clocks went forward, takes the offset from before the change.
- Without a `time` (a date alone, or `timeUnknown: true`) the zone is read at local noon.
- Refused with `400 INVALID_FIELD`: an empty value, abbreviations such as `EST` or `PST`, offsets written as text such as `+03:00`, names the database does not know, and `auto` without coordinates. `UTC` and `GMT` are accepted.
- Each object resolves on its own, so `chart1` and `chart2` can sit in different zones.

<Aside type="note">
Before 1970 the tz database is not reliable for every place: Amsterdam in June 1930 comes back as +1, where the clocks read +0:20. If the local clock of that birth is known, send `timezoneOffset`.
</Aside>

## `houseSystem`: a letter, not a name

Exactly these 25 Swiss Ephemeris codes are accepted:

`P` `K` `R` `C` `E` `W` `B` `M` `O` `A` `T` `V` `D` `F` `G` `H` `I` `i` `L` `N` `Q` `S` `U` `X` `Y`

Case matters: `I` is Sunshine by Makransky, `i` by Treindl. A system name (`"Placidus"`, `"Koch"`) returns `400`. It used to work by accident, because the engine reads only the first letter of the string, and by the same accident `"Zodiac"` quietly produced Placidus.

## Unknown birth time

Instead of inventing noon, send `timeUnknown: true` and omit `time`. Then `houses`, `houseAspects`, `chartSect` and `siderealTime` come back `null` rather than invented. Details: [API conventions](/en/api-conventions/).

## Unknown keys are not refused

The body accepts extra fields silently: an unrecognised key is simply ignored. So a typo in a field name that is not on the refusal list above will not announce itself. Check against the specification: [`/v1/openapi.json`](https://api.astroway.info/v1/openapi.json).

## Next

- [Common mistakes](/en/agent-setup/mistakes/)
- [Gotchas](/en/agent-setup/gotchas/)
