Bad Request (400)

The request was understood but rejected before any effect. The detail field names the first problem found. It can quote a value you sent (a site in origins, for example), so log it as you would log the request.

Common causes

  • Missing Idempotency-Key header on a POST (required on every POST).
  • A required field missing or with the wrong type (name, church_id, mode, value, url, event_types).
  • An optional field in the wrong format, such as a donor_phone that is not E.164 (+15551230000).
  • A value outside its range. With mode: "pct", a whole percentage from 1 to 35 (12, never 12.5); with mode: "fixed", dollars from 1 to 20,000 in whole cents (12.50, never 12.345).
  • event_types containing a type that is not subscribable.
  • A type on GET /v1/events that is not an activity event type: a typo, a financial type such as donation.updated (read it from GET /v1/financial_events), or type sent more than once. detail lists the accepted values.
  • An invalid since_cursor on the events feed — cursors are opaque; always pass back exactly what next_cursor gave you. A cursor also works only with the same type filter it was issued with.
  • A query parameter the operation does not accept, such as cursor on /v1/events (the feeds page with since_cursor). detail lists the ones it accepts.
  • A sandbox test EIN (00-0000001 to 00-0000099) sent when creating a church — test EINs go in the verification request. In POST /v1/churches/import the batch still answers 200: that row comes back rejected with the same detail, and the other rows are imported.
  • A sandbox simulator whose prerequisite is not met — detail says which one.

A body the API cannot read is also a 400 with this type, never a 413 or a 415. The detail says what to fix:

detail Cause
request body is not valid JSON The body does not parse as JSON
send the request body as JSON, with Content-Type: application/json A body sent without Content-Type: application/json, such as text/plain
request body is over the 1 MiB limit The body is larger than 1 MiB
request body uses a reserved JSON key name; remove that key and send the request again Valid JSON with a reserved key name, such as __proto__
Content-Length does not match the request body The Content-Length header disagrees with the body
request rejected Any other body the API refuses before reading it

An empty body with Content-Type: application/json counts as no body, so operations without a body, such as DELETE /v1/webhook_endpoints/{id}, accept it.

A path the gateway in front of the API cannot parse, such as one containing %zz, gets 400 with an HTML body instead of this page's problem body, and without X-Request-Id: it never reached the API. Fix how you build the URL (percent-encode ids before placing them in a path); see API errors.

What to do: fix the field named in detail and retry with the same Idempotency-Key — a request refused with 400 does not hold the key. If an earlier attempt with that key ended in a 5xx or a timeout, the key stays with that first body: send the corrected request with a new key.