API Reference
Endpoints, authentication, limits, error codes, and retry behavior for the Custom Push API
Complete technical reference for the Custom Push API. For an introduction, see Bring Your Own App.
Base URL
https://external.harmony.ioAll requests must use HTTPS.
Authentication
Every request carries your access key as a bearer token:
Authorization: Bearer <access key>Access keys are provisioned by Harmony support — contact support to request one. The key is scoped to your workspace; you never send a workspace or tenant identifier, and any such value in the request is ignored.
Store the key in a secrets manager or an environment variable. Never commit it to source control, never place it in a URL, and never expose it in client-side code.
Endpoints
Both accept Content-Type: application/json.
Limits
Rows per request
100 (users or events)
Metadata per row
1 KB serialized as JSON
Text fields
255 characters
Email fields
254 characters
App ID
1–200 characters, A–Z a–z 0–9 . _ -
Rate limiting
Applied per workspace; a throttled request returns 429
Requests that exceed the row limit or the metadata limit are rejected with a 400 validation error naming the offending field. Requests that are too large overall are rejected with 413.
Success responses
Both endpoints return 202 Accepted on success. 202 means the request was received and validated — processing happens asynchronously, so the data will not be visible in Harmony immediately.
Users:
Usage:
batch_id
Unique identifier for this request. Quote it when contacting support.
snapshot_id
Users only. The identifier of the user list this push opened or joined. Echo it as snapshot_token to continue a list across requests.
accepted_rows
Number of rows accepted.
Errors
Every error response has the same envelope. error is a stable machine-readable code — match on it rather than on the human-readable text, which may change.
Configuration errors — 400
Your request was well-formed, but the application it refers to is not set up correctly. Fix the configuration in Harmony, then retry.
unknown_app
No Bring Your Own App integration is registered with this App ID
Check that the App ID in Settings > Integrations matches your external_app_id exactly, including case
app_disabled
An integration is registered for this App ID but is disabled
Re-enable it in Settings > Integrations
ambiguous_app
Two or more enabled integrations use the same App ID
Disable the duplicate — one integration per application
Validation errors — 400
validation
One or more fields failed validation. The details array identifies each problem.
Each entry gives the exact path to the offending value — ["users", 0, "email"] means the email field of the first user in the array. No rows are accepted when validation fails: fix every reported problem and resend the whole request.
Snapshot token errors — 400
Only apply to users pushes that set snapshot_token.
malformed_snapshot_token
The token is not a value Harmony issued
Use the snapshot_id returned by the first request of the sequence. Tokens cannot be invented.
stale_snapshot_token
The user list this token refers to was left incomplete too long
Start over: send the complete user list again with no snapshot_token
future_snapshot_token
The token is not valid
Start over with no snapshot_token
Authorization errors — 403
integration_access_denied
Your access key does not carry the permissions this API requires
Contact Harmony support. This is a key provisioning issue, not a problem with your request — no change to your code will resolve it.
Other status codes
401
Missing or invalid access key
Check the Authorization header format: Bearer <key>
404
Unrecognized path
Verify the endpoint URL
413
Request body too large
Send fewer rows per request, or reduce per-row metadata
429
Rate limited
Wait, then retry with exponential backoff
502
Harmony could not accept the batch
Retry with backoff. See the warning below for users pushes.
503
A Harmony service is temporarily unavailable
Retry with backoff. Nothing was accepted, so no partial data landed.
Retry guidance
400
No
Fix your request or your Harmony configuration first. Retrying unchanged returns the same error.
401, 403
No
Credential problem. Contact support for 403 integration_access_denied.
413
No
Reduce the request size.
429
Yes
Back off, then retry.
502
Yes
Retry with exponential backoff.
503
Yes
Retry with exponential backoff. The request was rejected before anything was stored.
A reasonable policy is three attempts with exponential backoff and jitter, retrying only on 429, 502, and 503.
Retrying a failed users push: if a users push fails partway through a multi-request sequence, do not resume from where it stopped. Start the whole sequence again from the beginning without a snapshot_token. This opens a fresh, complete user list that replaces the incomplete one.
Resuming — or abandoning a half-sent list — leaves Harmony holding a partial user list that it treats as complete, which removes every user that never arrived. See Users data.
Usage pushes carry no such risk. Because every record is deduplicated on your own event_id, retrying a usage push — in whole or in part — is always safe.
Idempotency
/users
Each complete push replaces the previous user list. Sending the same list twice produces the same result.
/usage
Records are deduplicated by event_id. Sending the same records twice counts them once.
Neither endpoint uses an idempotency key header — idempotency comes from the data itself. On the usage side this depends entirely on deriving event_id deterministically; see The event ID contract.
Complete request examples
Users:
Usage:
Versioning
The API version is part of the path (/v1/). Request bodies also carry an optional schema_version field, currently "1.0".
Additive changes — new optional fields, new enum values — may be introduced within v1. Build clients that ignore unrecognized fields in responses. Breaking changes would arrive under a new path version, with the existing one supported through a published transition period.
See also
Bring Your Own App — overview and setup
Users data — user list reference
Usage data — activity reference
Last updated
Was this helpful?
