Field Types & Validation
The type and format each field expects, what the API accepts, and how validation errors are reported.
Every write endpoint type-checks its request body before anything is saved. A field with the wrong type is rejected with 400 and a message naming the field, what it expected, and what you actually sent - the whole request fails, so you never end up with a half-written record.
{
"error": "Invalid request body",
"details": [
"'track' must be a whole number (received the string \"Main Stage\").",
"'date' is not a real calendar date (received the string \"2026-02-30\")."
]
}
Fields to check before your first write
Most fields are plain text. These few take a number or a particular format, so they are worth confirming when you build your payload.
| Field | Object | Type | What it actually is |
|---|---|---|---|
track |
session | integer | The position of the track in your event’s track list, counting from 0. Put the stage or room name in location. |
tintColor |
session | integer | The session colour. Send a hex string such as "#3366FF", or a 32-bit ARGB integer if you already have one (4294967295 is white). |
tier |
sponsor | integer | The position of the tier in your event’s sponsor-tier list, counting from 0. Tier names are set under Event Settings. |
tags |
session, speaker | string | One tag, or several separated by commas. An array is accepted too. |
representativeEmails |
sponsor | string | One address, or several separated by commas. An array is accepted too. |
ticketRedemption |
session | string | The ticket tier that can be redeemed at this session. Leave empty for none. |
options |
poll | array of strings | A real JSON array, at least 2 entries - not comma-separated, because an option may itself contain a comma. |
maxChoices |
poll | integer | How many options one attendee may pick. Never more than the number of options. |
status |
question | enum | Exactly one of pending, approved, rejected, answered. Replaces the stored approved/rejected/answered booleans. |
points, bonusPoints |
score object | number | Decimals allowed; rounded when the leaderboard is calculated. |
trackArray, tierArray |
event | array of strings | The track and tier names. track and tier above are positions in these lists. |
Accepted formats
The API is deliberately forgiving where the intent is unambiguous, so you rarely have to match the internal storage format exactly.
| Type | Accepted | Stored as |
|---|---|---|
| integer | 150, "150" |
150 - decimals and non-numeric strings are rejected |
| boolean | true, "true", "yes", 1 / false, "false", "no", 0 |
true / false |
| colour | 4281558783, "#3366FF", "#FF3366FF", "0xFF3366FF" |
ARGB integer - a 6-digit hex gets a fully opaque alpha |
| tag list | "ai, keynote" or ["ai", "keynote"] |
"ai, keynote" - trimmed, blanks dropped, duplicates removed |
| email list | "[email protected], [email protected]" or ["[email protected]", "[email protected]"] |
"[email protected],[email protected]" - each address is validated |
" [email protected] " |
"[email protected]" - trimmed and lowercased |
|
| date | "2026-09-14" |
Same. Must be a real calendar date; "" clears it |
| time | "09:00", "9:00" |
"09:00" - 24-hour; "" clears it |
| rich text | Delta JSON string, or plain text | Same - malformed Delta JSON is rejected. See Rich Text Format |
| number | 10, 2.5, "2.5" |
2.5 - unlike integer fields, decimals are kept |
| string array | ["Yes", "No"] |
Same - entries are trimmed; blanks and duplicates are rejected |
| enum | "answered", "Answered" |
The canonical spelling. An invalid value is rejected with the full list of valid ones |
A few more rules worth knowing:
- Omitted means unchanged. On an edit, only the fields you send are written. To clear a field send
"",0, orfalse- notnull, which is rejected. - The body must be a JSON object. Send
Content-Type: application/json. A bare string or array is rejected with400. - Required fields are enforced on create. Creating a speaker without a
name, or a poll withoutoptions, is rejected with400before anything is written. On an edit the same fields are optional - but sending one empty is still rejected, since that would wipe it. - Writes are
POST, reads areGET. The wrong verb returns405with anAllowheader. - Test before you write. Add
?dryRun=1to any write to validate it - reference checks included - without saving. See Discovery & Dry Runs.
Warnings and ignored fields
Not everything that looks wrong is fatal. When a write succeeds but something is worth a second look, the 200 response carries extra keys:
| Key | Meaning |
|---|---|
warnings |
The value was saved, but probably isn’t what you meant - a link that isn’t an absolute http(s) URL, or a timeEnd before its timeStart. |
ignoredFields |
Body keys the endpoint does not recognise at all, so they were dropped. Near-misses get a suggestion, e.g. "unlisted (did you mean 'unListed'?)". |
{
"status": "success",
"sessionId": "aB3xY...",
"created": true,
"warnings": ["'handOutLink' does not look like an absolute http(s) URL (\"www.example.com/deck.pdf\"); it may not open as a link in the app or website widgets."],
"ignoredFields": ["colour", "unlisted (did you mean 'unListed'?)"]
}
Both keys are omitted entirely when there is nothing to report, so a clean request gets the same response shape it always did. ignoredFields is the quickest way to catch a typo, or a payload built against a different system.
Protected fields
A few fields would be a lie to accept. Attendee poll votes, Q&A upvotes and question scores are written by the app; billing limits and tenancy are not yours to set; and a question’s approved/rejected/answered flags have a supported replacement in status. Dropping any of those quietly would be worse than useless - a caller sending votes would get a 200 and reasonably believe it had recorded them.
So they are rejected with a 400 that names the reason and, where there is one, the field to use instead:
{
"error": "Invalid request body",
"details": [
"'votes' cannot be written through the API - attendee ballots are cast in the app and would be overwritten. Read results with /getPollResults.",
"'approved' cannot be written through the API - moderation state is set with the 'status' field (pending | approved | rejected | answered)."
]
}
This is the difference between an unrecognised key, which lands in ignoredFields, and a recognised but forbidden one, which fails the request outright.
Read-modify-write still works
The protected list is deliberately narrow, and no field that a read hands back to you is on it. Server-owned bookkeeping - documentId, clientId, deleted, hidden, apiImported, updated - and derived values like a session’s speakers array are simply reported in ignoredFields. So fetching an object, changing one value, and posting the whole thing back is safe: the fields you didn’t mean to write are listed, not fatal.
Anything genuinely protected is either never returned at all (poll votes, question voters) or has an obvious supported alternative, so a round trip can’t trip over one.
Every object’s protected fields, with their reasons, are listed by GET /apiSchema.
Cloudflare vs. the API
api.event-vault.com sits behind Cloudflare, which rejects unrecognised user agents before the request reaches the API. Always send a normal User-Agent header.
Tell the two apart by the response body: a 403 with a JSON body is a genuine permission or scope failure from the API; a 403 with an HTML body or an error code: 1010 is Cloudflare.