OperationsIntegration Preview
Error Handling
Error response structure, error codes and safe retry strategies.
Preview
Error codes and response structure are illustrative. No rate limit values are committed in this documentation; limits for your account are shared in your integration documentation.
ReinoAds uses standard HTTP status codes and returns a machine-readable code field with every error. A decision that ends in blocked_by_policy or no_fill is not an error; those are returned as an outcome inside a 200 response.
{
"error": {
"code": "invalid_placement",
"message": "Placement 'rewarded_v2' was not found in this environment.",
"param": "placement",
"request_id": "req_01HZXD8W2K"
}
}| code | HTTP | Meaning |
|---|---|---|
| invalid_request | 400 | The request body is invalid or a required field is missing. |
| missing_consent | 400 | The consent object was not provided. |
| authentication_failed | 401 | The API key is missing, invalid or revoked. |
| permission_denied | 403 | The key is not authorized for this operation, e.g. a publishable key calling a management endpoint. |
| environment_mismatch | 403 | A sandbox key tried to access a live resource, or vice versa. |
| invalid_placement | 404 | The placement was not found in this environment. |
| conflict | 409 | A resource with the same name already exists, or concurrent updates collided. |
| rate_limited | 429 | Too many requests were sent in a short period. |
| internal_error | 500 | An unexpected error occurred on the ReinoAds side. |
| service_unavailable | 503 | The service is temporarily unavailable. |
Retries
- Don't retry 4xx errors (except 429); fix the request instead.
- Retry 429, 500 and 503 with exponential backoff and jitter; honor the Retry-After header when present.
- Send an Idempotency-Key header on POST requests to management endpoints to avoid creating duplicates.
- If a decision request times out or fails, leave the slot empty; never make your user experience depend on an ad response.
async function requestWithRetry(body: unknown, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const res = await fetch("https://api.reinoads.com/v1/decisions", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.REINOADS_SECRET_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
if (res.ok) return res.json();
if (![429, 500, 503].includes(res.status)) throw await res.json();
const retryAfter = Number(res.headers.get("Retry-After"));
const delay = retryAfter > 0
? retryAfter * 1000
: 2 ** i * 250 + Math.random() * 100;
await new Promise((r) => setTimeout(r, delay));
}
return { outcome: "no_fill" };
}Contacting support
When contacting support, include the request_id from the error response and the decision ID (dec_...) if available. These values contain no personal data.
