# DeepAPI Docs — API Reference

> Agent-readable DeepAPI docs. Index: https://deepapi.co/llms.txt · OpenAPI: https://deepapi.co/openapi.json · Skill: https://deepapi.co/deepapi-skill/SKILL.md
> Markdown pages: https://deepapi.co/docs/setup.md · https://deepapi.co/docs/reference.md · https://deepapi.co/docs/pricing.md

## Basics

- Base URL: `https://deepapi.co`
- Auth: `Authorization: Bearer <key>` on every request.
- Idempotency: every POST sends an `Idempotency-Key` — any unique name you make up. Retrying with the same name returns the stored result instead of paying twice.
- Cost cap: every scrape sets `maxCostUsd`, the most that call is allowed to cost.
- Email setup: none. Your first call to `/v1/email/send` creates a managed email address for your workspace — nothing to connect. The response returns it as `output.emailIdentity.emailAddress`; replies to it show up in `/v1/email/messages`.
- Email drafts: `/v1/email/send` creates a draft by default and returns its `output.draftId`. Review pending drafts with `GET /v1/email/drafts`, then send one exactly as stored with `POST /v1/email/drafts/{draftId}/send`. Sending re-checks policy and requires direct-send approval for your workspace.

## Endpoints

### Scrape

- `POST /v1/scrape/website` — Crawl website pages and return clean page content. output is an array of page objects, each carrying the requested content (markdown and/or text — pick one with contentFormat, omit for both) plus a url when the origin reports one. Content-free pages are dropped, not billed. Optional per-page metadata: title, description, and language when the page provides them; truncated and totalChars when maxChars caps a page. For deep crawls, optional maxDepth and includeUrls/excludeUrls glob patterns steer which links are followed. If the origin answers but returns nothing usable, the call is free (debitMicrousd 0): output is an empty array and list.listState reports no_results, or source_blocked when blocking dominated (a login wall, captcha, rate limit, access-denied notice, or HTTP 401/403/407/429/451/503). A billed run whose content is still settling returns status succeeded with output null and a polling next — follow next until it is absent, regardless of status; a backend failure with no working fallback ends status failed, never a free empty success.
- `POST /v1/scrape/linkedin/profile` — Scrape public LinkedIn profile details.
- `POST /v1/scrape/github/profile` — Scrape public GitHub user or organization profile details.
- `POST /v1/scrape/github/repo` — Read public repository metadata, README, languages, license, topics, and statistics.
- `POST /v1/scrape/github/issues` — List and filter public repository issues, excluding pull requests.
- `POST /v1/scrape/github/pulls` — List public repository pull requests with merge state, authors, and diff statistics.
- `POST /v1/scrape/github/search` — Search public repositories, issues, pull requests, or code.
- `POST /v1/scrape/github/contents` — Read a public repository file or directory listing at a branch, tag, or commit.
- `POST /v1/scrape/github/commits` — List public repository commit history with author, path, and date filters.
- `POST /v1/scrape/github` — alias — Backward-compatible alias for GitHub profile scraping.
- `POST /v1/scrape/twitter/search` — Scrape X/Twitter posts from a search query or account handles.
- `POST /v1/scrape/linkedin/jobs` — Scrape public LinkedIn job listings for a search query. Requests must allow at least 10 postings: maxItems below 10 and cost caps below $0.04 are rejected.
- `POST /v1/scrape/linkedin/company` — Scrape public LinkedIn company pages for firmographic details.
- `POST /v1/scrape/linkedin/people` — Search public LinkedIn profiles by role, location, company, or school. maxCostUsd defaults to 1.00 (2.00 with includeDetails) and cannot go lower.
- `POST /v1/scrape/linkedin/posts` — Scrape recent public posts from LinkedIn profiles or company pages.
- `POST /v1/scrape/twitter/user` — Scrape public X/Twitter account profiles, with optional follower and following lists.
- `POST /v1/scrape/twitter/replies` — Scrape the public reply thread of an X/Twitter post. maxCostUsd defaults to 1.00; values below 0.40 are rejected.
- `POST /v1/scrape/youtube/transcript` — Scrape a YouTube transcript as plain text. Set includeSegments false for compact output; omission preserves timed segments for backward compatibility. Long transcripts: bound output with maxChars; truncated: true marks a capped result. Videos without captions return an empty result.
- `POST /v1/scrape/youtube/channel` — Scrape a YouTube channel's stats and recent videos. Each video item includes subscriber and channel totals.
- `POST /v1/scrape/youtube/search` — Search YouTube videos by keyword and return video metadata.
- `POST /v1/scrape/youtube/shorts` — Scrape a YouTube channel's Shorts feed. Returns short-form videos only (long-form videos and streams are excluded); each short carries the channel's stats. Use the transcript endpoint with a shorts URL to read a short's spoken content.
- `POST /v1/scrape/instagram/profile` — Scrape public Instagram profile details such as bio, follower counts, links, published business location, and related (similar) accounts.
- `POST /v1/scrape/instagram/posts` — Scrape recent public posts and reels from Instagram profiles, with captions and engagement counts.
- `POST /v1/scrape/instagram/comments` — Scrape public comments from an Instagram post or reel.
- `POST /v1/scrape/instagram/hashtag` — Find public Instagram posts or reels by hashtag. Results are bounded across the whole request.
- `POST /v1/scrape/facebook/ads` — Scrape ads from the Meta Ads Library — every ad running across Facebook, Instagram, Messenger, and Audience Network — by keyword or advertiser page. Returns ad creatives, copy, landing URLs, run dates, platforms, and EU transparency data. Ads cost ~$0.003 each: go deep with maxItems 100+ and combine advertiser pages with keyword queries. Requests must allow at least 10 ads: maxItems below 10 and cost caps below $0.03 are rejected.
- `POST /v1/scrape/reddit/search` — Search public Reddit posts by keyword across all of Reddit, or scoped to one or more subreddits, with sort (relevance, hot, top, new, comments) and time filters (hour to year).
- `POST /v1/scrape/reddit/posts` — Scrape recent public posts from one or more subreddits, with hot/new/top ordering and a since timeframe (24h to year) for top.
- `POST /v1/scrape/reddit/comments` — Scrape the public comment thread of a Reddit post, nested replies included (depth 0 = top-level). Fewer comments than maxItems can come back: a post's advertised count includes deleted and collapsed branches.
- `POST /v1/scrape/reddit/user` — Scrape public Reddit user profiles: karma breakdown, account age, verification, and follower count.
- `POST /v1/scrape/google/places` — Search public Google Maps business listings by term and optional location. Returns name, category, address, phone, website, rating, review count, and coordinates per place. Narrow results with categoryFilterWords, or fetch specific places by placeIds or urls.
- `POST /v1/scrape/linkedin` — alias — Backward-compatible alias for LinkedIn profile scraping.
- `POST /v1/scrape/twitter` — alias — Backward-compatible alias for X/Twitter search scraping.
- `POST /v1/scrape/pdf` — Extract the text of a public PDF URL: full text plus title, author, and page count, returned synchronously at a fixed price per document.

### Email

- `POST /v1/email/send` — Create an email draft from a workspace email identity; set send=true to send it.
- `GET /v1/email/messages` — Read messages for a workspace email identity.
- `GET /v1/email/drafts` — List pending email drafts for a workspace email identity.
- `GET /v1/email/identities` — List the workspace email identities and the emailIdentityId values other email routes accept.
- `POST /v1/email/drafts/{draftId}/send` — Approve and send an existing draft by draftId after review.
- `POST /v1/email/domains` — Add a customer-owned domain to send email from, and get the DNS records to publish.
- `GET /v1/email/domains` — List the workspace's customer-owned sending domains with status and pending DNS records.
- `POST /v1/email/domains/{domainId}/verify` — Re-check the domain's DNS records and refresh its verification status.
- `DELETE /v1/email/domains/{domainId}` — Remove a custom sending domain and suspend the identities on it.
- `POST /v1/email/identities` — Create a sender identity (optionally on a verified custom domain) and make it the workspace default.
- `PATCH /v1/email/identities/{emailIdentityId}` — Update an email identity: change its sender display name, or enable the recurring renewal that keeps a trial inbox.

### Research

- `POST /v1/research/deep` — Answer a research question with current web evidence.

### Generate

- `POST /v1/generate/image` — Generate an image from a text prompt.

### Search

- `POST /v1/search/web` — Search the web and return ranked results with title, url, snippet, and dateText when the source reports a date, plus a direct answer when one exists.

### Audio

- `POST /v1/transcribe` — Convert uploaded audio into plain text.
- `POST /v1/transcribe/uploads` — Create a temporary signed upload for one audio file.

### Memory

- `GET /v1/memory` — List the markdown files in this workspace's hosted memory, with sizes, versions, and usage against the limits.
- `POST /v1/memory/{path}` — Create or update one memory file. Writes replace the whole file and bump its version.
- `GET /v1/memory/{path}` — Read one memory file: full markdown content plus its current version for safe writes.
- `DELETE /v1/memory/{path}` — Delete one memory file permanently.

### Browser

- `POST /v1/browser/act` — Run a goal-driven task in a real cloud browser and return its result. (not configured)

### Account

- `GET /v1/balance` — Read the workspace credit balance without spending anything.
- `GET /v1/me` — Read what this API key can do: workspace, scopes, spend limits, remaining key budget, rate limits, and balance.
- `GET /v1/capabilities` — List every DeepAPI capability with its live status, or pass capability=<slug> to read one capability's full live contract.
- `GET /v1/usage` — Read workspace spend totals, a gap-filled per-day series, and a per-capability breakdown over the last sinceDays calendar days, counting today as day one.

### Requests

- `GET /v1/requests` — List recent requests created by this API key, newest first. Recovers lost requestIds.
- `GET /v1/requests/{requestId}` — Poll a request by requestId until its GET polling next action is absent (output can settle after status turns succeeded).

### Feedback

- `POST /v1/feedback` — Send a bug report, idea, or praise to the DeepAPI team. Free, any active key.

## Example requests

Scrape a website — `maxCostUsd` caps what the call may spend.

```bash
curl -X POST "$DEEPAPI_API_BASE_URL/v1/scrape/website" \
  -H "Authorization: Bearer $DEEPAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: scrape-$(uuidgen)" \
  -d '{
    "maxCostUsd": "0.10",
    "waitForFinishSecs": 60,
    "urls": ["https://example.com"],
    "maxPages": 1
  }'
```

Draft an email — stays a draft unless `send` is true. No inbox IDs; the key resolves the identity. The response returns `output.draftId`.

```bash
curl -X POST "$DEEPAPI_API_BASE_URL/v1/email/send" \
  -H "Authorization: Bearer $DEEPAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: email-$(uuidgen)" \
  -d '{
    "to": "person@example.com",
    "subject": "Quick hello",
    "text": "Hi, this is a draft from my agent.",
    "send": false
  }'
```

Review, then approve & send — list pending drafts, pick one, and send it exactly as stored. The body stays empty; the draft already holds recipients and content.

```bash
curl "$DEEPAPI_API_BASE_URL/v1/email/drafts" \
  -H "Authorization: Bearer $DEEPAPI_API_KEY"

curl -X POST "$DEEPAPI_API_BASE_URL/v1/email/drafts/draft_123/send" \
  -H "Authorization: Bearer $DEEPAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: approve-$(uuidgen)" \
  -d '{}'
```

## Response envelope

```json
{
  "requestId": "...",
  "route": "/v1/scrape/website",
  "capability": "scrape.website",
  "status": "succeeded",
  "replayed": false,
  "costFinal": true,
  "debitMicrousd": 2500,
  "output": [],
  "next": null,
  "error": null,
  "balance": {
    "postedCreditsMicrousd": 1000000,
    "postedDebitsMicrousd": 2500,
    "activeReservationsMicrousd": 0,
    "availableMicrousd": 997500
  },
  "skillVersion": "777eb99a3d87"
}
```

`debitMicrousd` is USD micro-dollars — 2500 is $0.0025. If the response includes a polling `next` (a GET of `/v1/requests/{id}`), wait `next.afterSecs` seconds and call it; repeat while that polling `next` is present — even when `status` is already `succeeded` (output can still be settling).

## Custom sending domains

Send from your own domain instead of the standard DeepAPI address: `POST /v1/email/domains` registers it (one-time fee) and returns the DNS records to publish; `POST /v1/email/domains/{domainId}/verify` is free and repeatable; then `POST /v1/email/identities` with `domain` creates your sender address on it. The Email page walks through the same flow with copy-paste DNS records.

Domain already handles email (Google Workspace, Outlook, etc.)? Add a subdomain like `agent.yourcompany.com` instead of the root. Sending needs only the SPF and DKIM records, so existing email keeps working untouched — the MX record is only required if the agent should also RECEIVE mail on that domain.

A verified domain rides its own sending reputation and gets higher automatic send limits than the shared domain.

## Error codes

Every failed response carries `error.code`, `error.retryable`, and `error.retryAfterSecs`.

| Code | HTTP | Meaning | What to do |
| --- | --- | --- | --- |
| `missing_api_key` | 401 | No bearer API key on the request. | Send `Authorization: Bearer $DEEPAPI_API_KEY`. |
| `invalid_api_key` | 401 | The API key is unknown, revoked, or expired. | Ask the user for a valid key. Do not retry with the same key. |
| `missing_idempotency_key` | 400 | POST request without an `Idempotency-Key` header. | Send a unique `Idempotency-Key` and retry. |
| `missing_scope` | 403 | The API key lacks the scope in `error.requiredScope`. | Ask the user for a key with that scope. Do not retry unchanged. |
| `card_setup_required` | 403 | The workspace has not completed required card setup. | Ask the user to complete setup at https://deepapi.co/card-setup, then retry. |
| `invalid_request` | 400 | A request field is invalid; `error.field` names it. | Fix the field per `error.message`, then retry with a new `Idempotency-Key`. |
| `insufficient_credits` | 402 | The workspace balance cannot cover the requested spend cap. | Pause and point the user to https://deepapi.co/credits — a one-time top-up or Auto Top-Up both unblock it — then retry with the same `Idempotency-Key`. |
| `api_key_limit_exceeded` | 402 | A per-request or total spend limit on this API key blocks the request. | Lower `maxCostUsd`, or ask the user to raise the key limit. |
| `rate_limit_exceeded` | 429 | Too many requests, or too many failed auth attempts, this minute. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. |
| `upstream_rate_limited` | 429 | The upstream provider rate-limited the request. | Wait `error.retryAfterSecs`, then retry with a new `Idempotency-Key`. |
| `idempotency_conflict` | 409 | The same `Idempotency-Key` belongs to a request that is still in progress. | Wait `error.retryAfterSecs`, then retry with the same key to receive the finished outcome (success or failure is replayed). Use a new key to attempt the operation again after a failure. |
| `unknown_capability` | 404 | No such endpoint, scrape target, or kind. | Use a documented endpoint path — `GET /v1` lists every endpoint. Do not retry unchanged. |
| `method_not_allowed` | 405 | The endpoint exists but not with this HTTP method; the `Allow` header lists the supported methods. | Retry using a method from the `Allow` header. `GET /v1` lists every endpoint with its method. |
| `resource_not_found` | 404 | The requested resource is missing or inaccessible. | Check the resource identifier and access. Do not retry unchanged. |
| `capability_not_configured` | 501 | The route exists but has no backend configured. | Do not retry. Report this to the user. |
| `request_not_found` | 404 | No request with this id exists for this API key. | Check `requestId`. Poll only requests created with the same key. |
| `email_identity_not_found` | 404 | `emailIdentityId` does not belong to this workspace. | Omit `emailIdentityId` to use the workspace default identity. |
| `email_draft_not_found` | 404 | No such draft for this email identity. | List drafts via `GET /v1/email/drafts` and use a returned `draftId`. |
| `email_policy_rejected` | 403 | Send policy blocked the request: recipient rules, content rules, a paused workspace, or the daily/monthly send cap. Caps grow automatically with clean sending history. | Follow `error.message`. If a cap was reached, retry after the window resets or create a draft instead. |
| `email_not_configured` | 503 | The workspace has no active email inbox. | Create or enable the inbox at https://deepapi.co/email, then retry. Adding credits alone does not create an inbox. |
| `email_identity_confirmation_required` | 409 | This email operation needs a new paid inbox. Nothing was created or charged. | Ask the user to choose the sender name and approve `error.setupPriceMicrousd` for the first 30 days and `error.renewalPriceMicrousd` every 30 days. This is one recurring inbox price, not a separate setup fee. Retry with `confirmInboxCharge: true` and optional `username`/`displayName`. |
| `email_domain_not_found` | 404 | No custom sending domain with this `domainId` in this workspace. | List domains via `GET /v1/email/domains` and use a returned domain id. |
| `email_domain_not_verified` | 403 | The custom domain exists but its DNS records are not verified yet. | Publish the dnsRecords from `GET /v1/email/domains`, then `POST /v1/email/domains/{domainId}/verify` until `verified` is true. Checks are free. |
| `email_domain_limit_exceeded` | 403 | The workspace reached its custom sending domain limit. | Remove an unused domain via `DELETE /v1/email/domains/{domainId}`, then retry. |
| `email_domain_conflict` | 409 | This domain is already registered with DeepAPI email by another workspace. | Stop and tell the user. If they own the domain, they should contact support. |
| `pdf_too_large` | 403 | The PDF file exceeds the size limit (about 50 MB). Nothing was charged. | Use a smaller PDF or a URL that serves the document in parts. Do not retry unchanged. |
| `pdf_not_readable` | 422 | The URL did not yield readable PDF text: not a PDF, password-protected, corrupted, or a scanned image with no text layer. Nothing was charged. | Check the URL serves an unencrypted, text-based PDF. Scanned PDFs need OCR, which this route does not do. Do not retry unchanged. |
| `audio_too_large` | 413 | The audio file exceeds 25 MB. Nothing was charged. | Compress or split the audio, create a new upload, then retry. |
| `audio_upload_not_found` | 404 | The temporary audio upload is missing, expired, or already consumed. | Create and upload a new audio file, then retry with a new `Idempotency-Key`. |
| `audio_upload_failed` | 502 | DeepAPI could not read or delete the temporary audio upload. Nothing was charged. | Create and upload a new audio file, then retry with a new `Idempotency-Key`. |
| `audio_not_readable` | 422 | The uploaded file did not contain readable supported audio. Nothing was charged. | Check the file format and audio content, then retry with a new upload and `Idempotency-Key`. |
| `transcription_failed` | 502 | The transcription backend failed after the request started. Nothing was charged. | Retry the same `uploadId` with a new `Idempotency-Key` before the upload expires. |
| `memory_file_not_found` | 404 | No memory file exists at this path for this workspace. | List files via `GET /v1/memory` to see what exists. To create the file, POST it with `content`. |
| `memory_limit_exceeded` | 403 | The workspace memory quota blocks this write: too many files, a file over the per-file size limit, or the workspace total is full. | Delete or shrink memory files via `GET /v1/memory` and `DELETE /v1/memory/{path}`, then retry. |
| `memory_version_conflict` | 409 | The file changed since the version you sent as `ifVersion` — another agent wrote it first. | GET the file again, merge your changes into the latest content, and retry with the new version. |
| `browser_task_rejected` | 403 | Browser task policy blocked the request. Nothing was charged. | Rework the task to public-web actions only, then retry with a new `Idempotency-Key`. |
| `browser_task_failed` | 502 | The browser task failed or stopped after it started. Nothing was charged, but it may have completed some actions. | Review the task state, then use a new `Idempotency-Key` only if another attempt is safe. |
| `browser_task_state_unknown` | 502 | The browser backend did not confirm whether task creation succeeded. Nothing was charged, but the task may have started. | Do not retry automatically. Review external effects before using a new `Idempotency-Key`. |
| `request_failed` | 502 | The provider run for a started request failed. Failed calls are free: the credit hold is released, nothing is charged, and `debitMicrousd` is null. | Not retryable with the same `Idempotency-Key`. Start a new request with a new key if still needed. |
| `scrape_request_failed` | 502 | Unexpected server error while handling a scrape request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `search_request_failed` | 502 | Unexpected server error while handling a web search request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `research_request_failed` | 502 | Unexpected server error while handling a deep research request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `generate_image_request_failed` | 502 | Unexpected server error while handling an image generation request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `transcription_upload_request_failed` | 502 | Unexpected server error while handling an audio upload request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `transcription_request_failed` | 502 | Unexpected server error while handling an audio transcription request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `memory_request_failed` | 502 | Unexpected server error while handling a memory request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `browser_request_failed` | 502 | Unexpected server error while handling a browser task request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_draft_failed` | 502 | Unexpected server error while handling an email draft request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_send_failed` | 502 | Unexpected server error while handling an email send request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_retrieval_failed` | 502 | Unexpected server error while handling an email read request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_draft_send_failed` | 502 | Unexpected server error while handling a draft send request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_domain_request_failed` | 502 | Unexpected server error while handling an email domain request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `email_identity_create_failed` | 502 | Unexpected server error while handling an email identity create request. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `request_lookup_failed` | 502 | Unexpected server error while handling a request status lookup. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `request_list_failed` | 502 | Unexpected server error while handling a request list read. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `balance_lookup_failed` | 502 | Unexpected server error while handling a balance read. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `account_lookup_failed` | 502 | Unexpected server error while handling an account info read. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `usage_lookup_failed` | 502 | Unexpected server error while handling a usage summary read. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `capability_list_failed` | 502 | Unexpected server error while handling a capability list read. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |
| `feedback_submit_failed` | 502 | Unexpected server error while handling a feedback submission. Nothing was charged. | Wait `error.retryAfterSecs`, then retry with the same `Idempotency-Key`. If it keeps failing, check `GET /v1/health`. |

---

Rendered live from the DeepAPI capability catalog. skillVersion: 777eb99a3d87
