Errors
grovs uses HTTP status codes to say whether a request worked. A 2xx means it did. Treat the status code as the source of truth.
Most errors have a JSON body with an error message written for people. Show it, but do not parse it. The wording can change.
Where a program needs to tell failures apart, the body also has an error_code. Branch on error_code, never on error.
{
"error": "campaigns: 600 per minute",
"error_code": "rate_limited"
}{
"error": "Link not found"
}Status codes
| Status | Meaning |
|---|---|
200 | Success. |
202 | Accepted. The work continues in the background, such as a push send. Poll the resource for progress. |
400 | The request is malformed: a missing PROJECT-KEY, an ENVIRONMENT other than production or test, invalid JSON, an unreadable date or a bad cursor. |
401 | No scoped key, or it is unknown or revoked. |
402 | The event stream needs an active paid plan on grovs Cloud. |
403 | The credential is valid but not allowed here: a key from another project or instance, or a plan without the feature. |
404 | The resource does not exist, belongs to another environment, or the feature is not available on this install. |
409 | The resource is in the wrong state for this call, such as deleting a launched in-app message, or it changed since you read it. |
422 | Validation failed. error names the field or condition at fault. |
429 | A per-feature limit was reached. Retry later. See the error_code. |
503 | A dependency is unavailable, or you were rate limited. Back off and retry. |
5xx | Any other server error. Retry with backoff. If it continues, contact [email protected]. |
{
"error": "Forbidden"
}{
"error": "Link not found"
}Error codes
Branch on these error_code values.
error_code | Status | Meaning |
|---|---|---|
clickhouse_unavailable | 503 | Analytics storage is off or unreachable. Retry later. On a self-hosted install, check ClickHouse. |
clickhouse_stale | 503 | Analytics storage is up, but its rollups are being rebuilt. Retry later. |
path_generation_failed | 503 | grovs could not allocate a link path. Retry. |
rate_limited | 429 | A per-minute limit is used up: the server push budget, or 120 requests per key on segments and in-app messages. Nothing was created. Retry after Retry-After seconds, or next minute. |
enterprise_required | 403 | Server push, segments and in-app messages need an active paid plan on grovs Cloud. |
segment_busy | 429 | Too many segment queries are running. Retry in a few seconds. |
invalid_definition | 422 | A saved segment cannot be evaluated: it uses in_list before a list was uploaded, or purchase without revenue tracking. |
window_expired | 422 | A segment's "did not" condition reaches past your data retention. |
query_too_heavy | 422 | A segment query exceeded its time or memory budget. Narrow a window or remove a condition. |
upload_in_progress | 409 | Another identifier list upload for this segment is still running. |
{
"error": "Analytics temporarily unavailable",
"error_code": "clickhouse_unavailable"
}Rate limits
Over these per-minute limits, the answer is 503 with a plain-text body, not 429. Back off and retry. Do not retry in a tight loop.
| What | Limit |
|---|---|
| Event stream pull feed | 120 per est_ key |
| Audit log export | 60 per aet_ key |
Server push has its own per-project budget. Over it, the answer is 429 with error_code: "rate_limited" and a JSON body.
Segments and in-app messages share a limit of 120 requests a minute per project key. Over it, the answer is 429 with error_code: "rate_limited", a retry_after field and a Retry-After header, both in seconds.
Server Error{
"error": "Rate limit exceeded; retry in 23s",
"error_code": "rate_limited",
"retry_after": 23
}