## TL;DR
The 400 body names the invalid field, so start there instead of guessing. Validate the required fields for your event type and their types against the Sift events reference. The usual culprits are a wrong $type value, a missing required field, or a nested object where Sift expects a string.

```text
sift api error 400 invalid event structure on decisions api
```

## Use this when
- The Decisions API returns 400 with an invalid event structure message.
- You are sending a new event type for the first time.
- The error names a specific field.

## Not for this skill when
- The status is 401 or 403. That is a credentials problem.
- The Score API rejects the event. Validate against the Score API schema instead.
- The event is accepted but the decision looks wrong. That is a workflow problem.

## Steps
1. Log the full 400 response and note the field it flags. Verify: you can quote the exact field name.
2. Check the $type value matches a documented Sift event type exactly. Verify: the type string matches the reference, including the $ prefix.
3. Confirm every required field for that event type is present. Verify: $user_id and the other required fields exist in the payload.
4. Check field types: strings where strings are expected, numbers where numbers are expected, no nested objects in flat fields. Verify: each flagged field now matches the schema.
5. Resend the corrected event. Verify: the API returns 200 with a decision.

## Variant phrasings
### sift decisions api 400 bad event
Shorter phrasing.
### sift invalid event structure error
Error-text phrasing.
### sift event validation failed decisions
Validation-focused phrasing.

Compatibility: Sift Decisions API; event schema follows the Sift events reference. Applies to server-side event sends.

## Why it happens
Sift validates events strictly against per-type schemas before scoring. A typo in $type, a missing required field, or a wrong JSON type fails validation immediately. The 400 is precise about the problem, which makes this a fast fix once you read the body.

## Edge cases / pitfalls
- Reserved fields starting with $ have exact names. A near-miss like user_id without the $ is a different, invalid field.
- Some fields are required only for certain event types. Check the schema for your specific type, not the generic one.
- Client libraries sometimes serialize empty objects for unset fields. Omit unset fields instead of sending empty objects.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_hLr1Hn94R11vGvLZXU-ZtQ
