---
title: API objects and errors
description: Reference Stormedo response envelopes, request and schedule objects, pagination, identifiers, timestamps, error codes, and redaction behavior.
---

## Response envelope

Successful JSON responses use this envelope:

```json
{
  "success": true,
  "data": {},
  "error": null,
  "meta": null,
  "request_id": "95c07a5c-3d44-4b89-84a6-c5a71313bff2"
}
```

Error responses use the same top-level shape:

```json
{
  "success": false,
  "data": null,
  "error": {
    "code": "validation_error",
    "message": "Please check your input and try again.",
    "details": {
      "url": ["Destination URL must use HTTPS"]
    }
  },
  "meta": null,
  "request_id": "63bd4579-f03e-4a1e-8d98-2d38f6847b03"
}
```

Use `error.code` for program behavior. `error.details` is present only when structured details are available. Include the top-level `request_id` when contacting Stormedo support.

Transport-level failures such as a body rejected before handler execution can return an HTTP error without the Stormedo JSON envelope.

## Identifiers

| Prefix | Resource |
| --- | --- |
| `prj_` | Project |
| `req_` | Durable request |
| `sch_` | Recurring schedule |

IDs are opaque and case-sensitive. Do not parse or generate them.

## Timestamps

API timestamps are RFC 3339 strings with an explicit offset. Do not assume they always use the `Z` suffix because caller-provided offsets can be preserved. Optional lifecycle timestamps are null until that transition occurs.

## Enqueued request

Returned by quick send, advanced creation, replay, and each batch item.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Stable `req_` request ID. |
| `project_id` | string | `prj_` project selected by the API key. |
| `replayed_from_request_id` | string or null | Source request when created by replay. |
| `status` | string | Initially `queued` or `scheduled`. |
| `scheduled_at` | string or null | Absolute delivery time for delayed work. |
| `created_at` | string | Durable creation time. |

## Request summary

List responses return:

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Request ID. |
| `replayed_from_request_id` | string or null | Original request for replays. |
| `schedule_id` | string or null | Owning recurring schedule. |
| `url` | string | Destination URL. |
| `method` | string | Destination HTTP method. |
| `queue` | string | Queue label. |
| `status` | string | `scheduled`, `queued`, `delivering`, `succeeded`, `failed`, or `cancelled`. |
| `scheduled_at` | string or null | Planned delivery time. |
| `created_at` | string | Creation time. |
| `started_at` | string or null | First delivery start time. |
| `completed_at` | string or null | Final success or failure time. |

## Request list page

| Field | Type | Description |
| --- | --- | --- |
| `items` | array | Request summaries ordered newest first. |
| `next_cursor` | string or null | Opaque cursor for the next page. |
| `history_retention_days` | integer | Terminal request history included with the current plan. |
| `history_cutoff_at` | string | Current terminal-history cutoff. |
| `has_hidden_history` | boolean | Whether older terminal requests exist beyond the cutoff. |

## Request detail

Request detail includes every summary field plus:

| Field | Type | Description |
| --- | --- | --- |
| `max_attempts` | integer | Total attempt budget. |
| `timeout_seconds` | integer | Timeout for each attempt. |
| `metadata` | object | Publisher metadata, never forwarded. |
| `content_type` | string or null | Delivered content type. |
| `headers` | object | Destination header names with values replaced by `[REDACTED]`. |
| `updated_at` | string | Last projected lifecycle update. |
| `cancelled_at` | string or null | Cancellation completion time. |
| `attempts` | array | Completed attempt objects. |

Request bodies and destination header values are never returned.

## Delivery attempt

| Field | Type | Description |
| --- | --- | --- |
| `attempt_number` | integer | One-based attempt number. |
| `status` | string | `succeeded` or `failed`. |
| `response_status_code` | integer or null | Destination HTTP status when one was received. |
| `error_message` | string or null | Bounded delivery failure description. |
| `duration_ms` | integer | Attempt duration in milliseconds. |
| `started_at` | string | Attempt start time. |
| `completed_at` | string | Attempt completion time. |
| `retry` | object or null | Retry decision when another attempt was scheduled. |

Retry reasons are `network_error`, `http_408`, `http_429`, and `http_5xx`. Delay sources are `exponential_backoff` and `retry_after`.

When `retry` is present, it contains:

| Field | Type | Description |
| --- | --- | --- |
| `reason` | string | Why the completed attempt can be retried. |
| `retry_after_raw` | string or null | Bounded destination `Retry-After` value, when supplied. |
| `retry_after_truncated` | boolean | Whether the stored raw value was truncated. |
| `retry_after_escaped` | boolean | Whether unsafe characters were escaped in the stored raw value. |
| `retry_after_status` | string or null | `valid`, `invalid`, or `capped`. |
| `retry_after_delay_ms` | integer or null | Parsed and bounded destination delay. |
| `backoff_delay_ms` | integer | Stormedo's exponential-backoff delay. |
| `selected_delay_ms` | integer | Delay selected for the next attempt. |
| `delay_source` | string | `exponential_backoff` or `retry_after`. |
| `next_attempt_at` | string | Planned RFC 3339 time for the next attempt. |

## Schedule summary

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Stable `sch_` schedule ID. |
| `name` | string | Schedule display name. |
| `timing` | object | Resolved cron or interval definition. |
| `status` | string | `active` or `paused`. |
| `url` | string | Destination URL. |
| `retry` | integer | Additional retries per occurrence. |
| `next_run_at` | string or null | Next planned occurrence. |
| `last_run_at` | string or null | Most recent occurrence time. |
| `last_run_outcome` | string or null | `enqueued` or `skipped_usage_limit`. |
| `created_at` | string | Creation time. |
| `updated_at` | string | Last update time. |

Schedule detail replaces the top-level `url` and `retry` representation with a complete `request` object containing `url`, `body`, redacted `headers`, and `retry`.

## Cursor pagination

Request list cursors are opaque. Send `next_cursor` unchanged as the next request's `cursor`. Do not decode, edit, or store assumptions about its format.

## Error codes

| HTTP status | Error code | Meaning |
| --- | --- | --- |
| `401` | `unauthorized` | Authentication is missing or invalid. |
| `404` | `not_found` | The project-scoped resource does not exist or is no longer retained. |
| `409` | `conflict` | Resource state or idempotency input conflicts with the action. |
| `422` | `validation_error` | Request input is invalid. Field errors appear in `details`. |
| `429` | `plan_limit_exceeded` | A monthly request, schedule, or history allowance was exceeded. |
| `500` | `internal_error` | Stormedo encountered an internal failure. |
| `502` or `503` | `service_unavailable` | Durable execution or another required Stormedo dependency is unavailable. |

Plan-limit details can include `resource`, `used`, `limit`, `requested`, and `resets_at`.
