REST API
Authentication, errors and the main endpoints for scripts, backends and agents that don't use MCP.
Everything you do in the app, you can do over the REST API. It's what the MCP tools use too. For most agent work, MCP or the CLI is easier.
Basics
https://app.datahop.dev/api/projects/:projectId/…- Project id: it's in the app's URL (
/p/1/…), or askGET /api/me. - Auth:
Authorization: Bearer dh_sk_…, with an API key for that project. - JSON in and out.
PATCHchanges only the fields you send. - Times are ISO 8601 in UTC. Chart labels are in the project timezone.
- Endpoints that take a person accept a person id or a distinct id.
- Names in paths must be URL-encoded, for example
/events/%24pageview.
Errors
Every error looks like this:
{ "error": "invalid_query", "message": "Math \"sum\" needs mathProperty", "hint": "Set mathProperty to a numeric event property, e.g. \"amount\"" }Branch on error. Show or log message and hint: hints say how to fix the call.
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_query, invalid_request, … | Something in the request is wrong. Check the hint. |
| 401 | unauthorized | Missing or invalid key. |
| 403 | forbidden | A read-only key tried to write, the key belongs to another project, or only signed-in people can do this. |
| 403 | module_disabled | That product is switched off in Settings → Products. |
| 404 | not_found | It doesn't exist. |
| 409 | key_taken, in_use, invalid_state, experiment_running | A conflict: a flag key already exists, something is still in use, an experiment is in the wrong state, or you're changing a running experiment's variants. |
Run any analysis
POST /query takes any query object and returns the result with a summary.
curl -s https://app.datahop.dev/api/projects/1/query \
-H "Authorization: Bearer $DATAHOP_KEY" -H 'content-type: application/json' \
-d '{ "kind": "funnel", "steps": [{ "event": "signed_up" }, { "event": "plan_upgraded" }], "windowDays": 14 }'Results can be up to a minute old. Add ?refresh=1 to get fresh numbers.
Endpoints
Paths are relative to /api/projects/:projectId. W needs a read & write key.
| Area | Endpoints |
|---|---|
| Schema | GET /events · PATCH /events/:name W (description) · GET /properties?type=event|person|group · PATCH /properties/:type/:key W · GET /property-values?key= |
| Insights | GET /insights · GET /insights/:id · POST /insights W · PATCH /insights/:id W · DELETE /insights/:id W |
| Dashboards | GET /dashboards · GET /dashboards/:id · POST /dashboards W · PATCH /dashboards/:id W · DELETE /dashboards/:id W · POST /dashboards/:id/tiles W · PATCH /dashboards/:id/tiles/:tileId W · DELETE /dashboards/:id/tiles/:tileId W · PUT /dashboards/:id/share W |
| Persons | GET /persons?search=&cohortId= · GET /persons/:id · PATCH /persons/:id W ({ set, unset }) · DELETE /persons/:id W |
| Cohorts | GET /cohorts · GET /cohorts/:id · POST /cohorts W · PATCH /cohorts/:id W · DELETE /cohorts/:id W · GET /cohorts/:id/persons · POST /cohorts/:id/calculate · POST /cohorts/:id/members W · DELETE /cohorts/:id/members W |
| Groups | GET /group-types · PATCH /group-types/:index W (display names) · GET /groups?groupTypeIndex=&search= · GET /groups/:index/:key |
| Feature flags | GET /flags · GET /flags/:id · POST /flags W · PATCH /flags/:id W · DELETE /flags/:id W · POST /flags/evaluate |
| Experiments | GET /experiments · GET /experiments/:id · POST /experiments W · PATCH /experiments/:id W · POST /experiments/:id/launch W · POST /experiments/:id/stop W · POST /experiments/:id/conclude W · DELETE /experiments/:id W · GET /experiments/:id/results · POST /assist/experiment (draft from a description) |
| Feedback | GET /feedback?status=&cohortId=&search= · GET /feedback/stats · PATCH /feedback/:id W |
| Context | GET /annotations · POST /annotations W · DELETE /annotations/:id W · GET /activity |
| Project | GET /api/projects/:projectId · PATCH /api/projects/:projectId W (name, timezone) · GET /modules · PATCH /modules W (products on/off) |
Lists of persons and groups return { total, results } and page with limit (up to 200) and offset.
Creating and revoking API keys, rotating the project token and deleting the project are only possible for signed-in people in the app.
Examples
# create a cohort of power users
curl -s https://app.datahop.dev/api/projects/1/cohorts \
-H "Authorization: Bearer $DATAHOP_KEY" -H 'content-type: application/json' \
-d '{ "name": "Power users", "definition": { "match": "all", "criteria": [
{ "type": "behavior", "event": "project_opened", "withinDays": 14, "minCount": 20 } ] } }'
# widen a flag to 30% of everyone
curl -s -X PATCH https://app.datahop.dev/api/projects/1/flags/4 \
-H "Authorization: Bearer $DATAHOP_KEY" -H 'content-type: application/json' \
-d '{ "active": true, "filters": { "conditions": [{ "properties": [], "rolloutPercentage": 30 }] } }'
# conclude an experiment and roll out the winner
curl -s https://app.datahop.dev/api/projects/1/experiments/4/conclude \
-H "Authorization: Bearer $DATAHOP_KEY" -H 'content-type: application/json' \
-d '{ "winner": "test", "reason": "+26.9% upgrades, driven by new users.", "rollOut": true }'The fields for flags, experiments and cohorts match what you see in the app. Flag conditions use property filters, and cohort rules use cohort definitions.