Your AI Connector Docs

Analytics & Reports API

These read-only endpoints let you pull your account’s activity into your own dashboards and reports: message-event counts, credit consumption, AI spend, and the same charts and insights the in-app dashboard shows. This guide covers:

  • Summary — message-volume counters (sent, delivered, read, replied, booked, contacts created, credits).
  • Credits — a detailed, paginated ledger of credit usage with totals and breakdowns.
  • AI cost — a per-day rollup of AI spend.
  • Metric series — a chart-ready time series for one or more metrics, grouped by campaign, channel, AI Agent, or number.
  • Conversation outcomes — how conversations ended, by AI-assigned outcome tag.
  • Dashboard insights and Dashboard AI insights — the full data behind the in-app dashboard, including AI-written summaries.
  • Entity activity — a single contact’s, deal’s, or task’s timeline.
  • Aggregated event counts — a legacy, camelCase form of Summary kept for existing integrations.

Every endpoint on this page needs an exact scope, not both: pass at most one of campaign_id (legacy) or agent_id where the endpoint accepts it. Sending both returns 400, and an id that is not on your account returns 404 rather than 403, so other accounts’ ids stay unguessable.

All paths below are relative to the API base URL:

https://api.youraiconnector.com/v1

Every request must be authenticated. See Authentication for the four accepted methods. The examples here use the X-API-Key header (and one query-parameter form for cURL).


Date range

All three endpoints accept the same optional date filters:

Parameter Description
from Start of the range, YYYY-MM-DD, inclusive. Defaults to 30 days ago.
to End of the range, YYYY-MM-DD, inclusive. Defaults to today.

Dates are interpreted in UTC. The range defaults to the last 30 days and is capped at 366 days — a wider range returns 400. from must not be after to.

The truncated flag

The Summary and Credits endpoints cap how many records a single request scans. If your range is busy enough to hit that cap, the response includes "truncated": true. When you see it, the numbers are based on a partial scan — narrow your date range (or page through with a smaller window) to get complete figures.

Note: Cost and token figures are only included for AI calls billed to your own provider API keys. When cost figures are hidden for your account, the response sets "costs_redacted": true and the cost fields are returned as zero.


Message volume summary

Returns aggregated message-event counters for your account, both as range totals and as a per-day series. Every day in the range appears in by_date — quiet days are zero-filled. Optionally filter to a single campaign with campaign_id.

GET /analytics/summary

Parameter Required Description
from No Range start, YYYY-MM-DD.
to No Range end, YYYY-MM-DD.
campaign_id No Only count events belonging to this campaign.

cURL

curl "https://api.youraiconnector.com/v1/analytics/summary?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/summary?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/summary",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()

Response

{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "total": 1240,
    "sent": 800,
    "delivered": 760,
    "read": 540,
    "replied": 210,
    "booked": 35,
    "contact_created": 120,
    "credits_spent": 412.5,
    "credits_recharged": 500
  },
  "by_date": [
    {
      "date": "2026-05-01",
      "total": 40,
      "sent": 25,
      "delivered": 24,
      "read": 18,
      "replied": 7,
      "booked": 1,
      "contact_created": 4,
      "credits_spent": 13.5,
      "credits_recharged": 0
    }
  ],
  "truncated": false
}

Each entry in by_date has the same counter fields as totals, plus a date.

If you pass a campaign_id that does not belong to your account, the response is 404 with { "success": false, "error": "Campaign not found" }.


Credit usage

Returns credit usage over the range: a paginated list of individual records, plus range totals and breakdowns by reason and by campaign.

GET /analytics/credits

Parameter Required Description
from No Range start, YYYY-MM-DD.
to No Range end, YYYY-MM-DD.
campaign_id No Only include usage attributed to this campaign.
limit No Page size for records, 1–100. Defaults to 50.
cursor No Pass the previous page’s next_cursor to fetch the next page.

Adjustments vs. consumption: Balance changes such as bonuses, plan renewals, and corrections are excluded from totals and the breakdowns — they are not real consumption. They still appear in the records list, flagged with "is_adjustment": true.

Totals and breakdowns appear only on the first page (when no cursor is supplied). On later pages, totals, by_reason, by_reason_cost, and by_campaign are returned as null — only the records array continues. This avoids re-scanning the whole range for every page.

cURL

curl "https://api.youraiconnector.com/v1/analytics/credits?from=2026-05-01&to=2026-05-31&limit=50&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({
  from: "2026-05-01",
  to: "2026-05-31",
  limit: "50",
});
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/credits?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

// To page: pass data.next_cursor as ?cursor on the next request, until it is null.

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/credits",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31", "limit": 50},
)
data = res.json()

# To page: pass data["next_cursor"] as cursor on the next request, until it is None.

Response (first page)

{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "credits_used": 412.5,
    "cost_usd": 1.284512,
    "records": 318
  },
  "by_reason": {
    "AI Message": 380.0,
    "Campaign Message": 32.5
  },
  "by_reason_cost": {
    "AI Message": 1.284512,
    "Campaign Message": 0
  },
  "by_campaign": {
    "Spring Promo": 250.0,
    "Reactivation": 162.5
  },
  "records": [
    {
      "id": "rec_abc123",
      "amount": 1,
      "timestamp": "2026-05-31T14:02:11.000Z",
      "reason": "AI Message",
      "is_adjustment": false,
      "campaign_id": "campaign123",
      "campaign_name": "Spring Promo",
      "contact_id": "contact456",
      "contact_name": "Jane Smith",
      "credit_type": "ai",
      "custom_keys_used": false,
      "description": null,
      "cost_usd": 0,
      "input_tokens": 0,
      "output_tokens": 0,
      "cache_read_tokens": 0,
      "cache_creation_tokens": 0,
      "ai_model": null,
      "request_id": null,
      "is_test": false
    }
  ],
  "next_cursor": "rec_abc123",
  "costs_redacted": false,
  "truncated": false
}

Field notes:

  • amount — credits charged for the record. Zero for records billed to your own provider API key.
  • is_adjustmenttrue for balance changes (excluded from totals/breakdowns).
  • cost_usd, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, ai_model, request_id — populated only on records billed to your own provider API key; zero or null otherwise.
  • is_testtrue for playground/test runs, which are never billed.
  • next_cursor — the cursor for the next page, or null when there are no more records.

AI cost rollup

Returns the per-day AI spend rollup for your account. This reads pre-aggregated daily totals, so it is fast even over long ranges. Every day in the range appears in days — quiet days are zero-filled.

GET /analytics/ai-cost

Parameter Required Description
from No Range start, YYYY-MM-DD.
to No Range end, YYYY-MM-DD.

cURL

curl "https://api.youraiconnector.com/v1/analytics/ai-cost?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/ai-cost?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/ai-cost",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()

Response

{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "totals": {
    "total_usd": 12.4821,
    "byok_usd": 12.4821,
    "platform_usd": 0,
    "calls": 4210
  },
  "days": [
    {
      "date": "2026-05-01",
      "total_usd": 0.4012,
      "byok_usd": 0.4012,
      "platform_usd": 0,
      "input_usd": 0.18,
      "output_usd": 0.19,
      "cache_creation_usd": 0.02,
      "cache_read_usd": 0.0112,
      "calls": 140,
      "by_provider": { "anthropic": 0.4012 }
    }
  ],
  "costs_redacted": false
}

Field notes:

  • byok_usd — spend billed to your own provider API keys.
  • platform_usd — the portion of spend that ran on the platform rather than your own key.
  • input_usd, output_usd, cache_creation_usd, cache_read_usd — the cost components that make up total_usd.
  • by_provider — USD spend keyed by AI provider name.
  • USD figures are only returned to accounts that use their own provider key. For credit-paying accounts every USD field is zero and costs_redacted is true (call counts stay visible).

Metric series

Returns one or more metric time series in one call, optionally grouped by up to two dimensions — the endpoint to bind a chart to. A single request can answer “sent and replied per day, per channel, for this campaign” without one call per campaign.

GET /analytics/series

Every response carries a labels array (the time axis, zero-filled across the whole range) and one entry in series per group, each holding one array per requested metric aligned to labels. Series past limit are not dropped — they collapse into other_bucket, computed as the range total minus the returned series, so a rendered chart always adds up to your real numbers; truncated is true whenever that happens.

Where the numbers come from: sent, delivered, read, and replied come from message records, which carry the channel and sending number. booked, contact_created, and credits_spent come from the event stream, which carries no sending number, so those metrics land in the null-number bucket when you group by number.

Parameter Required Description
from No Range start, YYYY-MM-DD. Defaults to 30 days ago.
to No Range end, YYYY-MM-DD. Defaults to today.
metrics No Comma-separated list from sent, ai_sent, human_sent, delivered, read, replied, booked, contact_created, credits_spent. Defaults to sent,replied. An unknown metric returns 400.
group_by No Comma-separated list of up to two dimensions from date, campaign, channel, agent, number. date is accepted but has no effect — every response already carries the time axis. Omit for a single account-wide series.
granularity No day (default), week, or month. Week buckets start on Monday, month buckets on the 1st.
limit No How many series to return before the rest collapse into other_bucket, 1–50. Defaults to 12.
campaign_id No Only count activity belonging to this campaign. Legacy; prefer agent_id.
agent_id No Only count activity belonging to this AI Agent.
channel No Only count activity on this channel, for example whatsapp.

This endpoint’s date range is capped at 92 days (tighter than the 366-day cap used elsewhere on this page).

cURL

curl "https://api.youraiconnector.com/v1/analytics/series?from=2026-05-01&to=2026-05-31&metrics=sent,replied,booked&group_by=campaign,channel&limit=10&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({
  from: "2026-05-01",
  to: "2026-05-31",
  metrics: "sent,replied,booked",
  group_by: "campaign,channel",
  limit: "10",
});
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/series?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/series",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "from": "2026-05-01",
        "to": "2026-05-31",
        "metrics": "sent,replied,booked",
        "group_by": "campaign,channel",
        "limit": 10,
    },
)
data = res.json()

Response

{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "granularity": "day",
  "labels": ["2026-05-01", "2026-05-02"],
  "group_by": ["campaign", "channel"],
  "metrics": ["sent", "replied", "booked"],
  "series": [
    {
      "key": {
        "campaign_id": "campaign123",
        "campaign_name": "Spring Promo",
        "channel": "whatsapp"
      },
      "total": 812,
      "metrics": {
        "sent": [40, 35],
        "replied": [12, 9],
        "booked": [2, 1]
      }
    }
  ],
  "other_bucket": {
    "series_count": 6,
    "total": 340,
    "metrics": {
      "sent": [18, 20],
      "replied": [5, 6],
      "booked": [0, 1]
    }
  },
  "truncated": true
}

Field notes:

  • key — the identity of one series. Only the keys for the requested group_by dimensions are present; a dimension whose value is unknown for a row (a message with no campaign, an event with no channel) comes back as null rather than being dropped, so series still add up to the totals.
  • other_bucketnull when nothing was collapsed.
  • This endpoint returns 503 with "error_code": "analytics_unavailable" when the reporting database cannot answer for your account, rather than a 200 full of zeros — a zeroed chart would read as fact.

Conversation outcomes

Returns how conversations ended over a date range: a per-day count for each outcome tag the AI assigned, plus range totals for replies, bookings, handovers to a human, and conversations the AI never classified.

GET /analytics/outcomes

Pass group_by=tag to collapse the time axis and get range totals per tag only — in that mode labels is empty and each tag’s counts array is empty, while total is still populated.

Parameter Required Description
from No Range start, YYYY-MM-DD. Defaults to 30 days ago.
to No Range end, YYYY-MM-DD. Defaults to today.
campaign_id No Only count conversations with contacts currently on this campaign. Legacy; prefer agent_id.
agent_id No Only count outcomes belonging to this AI Agent.
group_by No date (default) keeps the per-day counts; tag collapses to range totals.

This endpoint’s date range is capped at 92 days.

cURL

curl "https://api.youraiconnector.com/v1/analytics/outcomes?from=2026-05-01&to=2026-05-31&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ from: "2026-05-01", to: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/outcomes?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/outcomes",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"from": "2026-05-01", "to": "2026-05-31"},
)
data = res.json()

Response

{
  "success": true,
  "from": "2026-05-01",
  "to": "2026-05-31",
  "group_by": "date",
  "labels": ["2026-05-01", "2026-05-02"],
  "by_tag": [
    { "tag": "interested", "total": 84, "counts": [3, 5] },
    { "tag": "not_interested", "total": 40, "counts": [1, 2] },
    { "tag": null, "total": 12, "counts": [0, 1] }
  ],
  "totals": {
    "sessions": 260,
    "replied": 210,
    "booked": 35,
    "human_alerted": 18,
    "unresolved": 12
  }
}

Field notes:

  • by_tag[].tagnull for conversations the AI never assigned an outcome tag to.
  • totals.human_alerted — conversations handed over to a human; this is written on every handover and was not previously surfaced by any endpoint.
  • Same 503/analytics_unavailable posture as Metric series when the reporting database cannot answer.

Dashboard insights

Returns the full dashboard payload for a date range in one call: a reply-rate heatmap by weekday and hour, the campaign leaderboard, per-channel volume, exact per-connection totals, per-day metric breakdowns (account-wide, per channel, and per number), where contacts are from, inbox response time, and a recent activity feed. This is the richest reporting payload on the API — it powers the in-app dashboard directly.

GET /analytics/dashboard-insights

Parameter Required Description
startDate Yes Range start, YYYY-MM-DD.
endDate Yes Range end, YYYY-MM-DD.
campaignId No Only include activity belonging to this campaign (campaign_id also accepted). Legacy; prefer agent_id.
agent_id No Only include activity belonging to this AI Agent (agentId also accepted). Under an agent scope, the campaign leaderboard is built from that agent’s activity only.

This endpoint uses startDate/endDate (not from/to) because it shares its implementation with the in-app dashboard. The range is capped at 92 days and is clamped, not rejected, when it is wider.

Null means unavailable, not zero. Several blocks (numberStats, channelDailySeries, metricDailyBreakdown, contactsByCountry) are computed from the reporting database and come back null when it cannot answer for your account. Do not render a null block as an empty chart.

cURL

curl "https://api.youraiconnector.com/v1/analytics/dashboard-insights?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ startDate: "2026-05-01", endDate: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/dashboard-insights?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/dashboard-insights",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"startDate": "2026-05-01", "endDate": "2026-05-31"},
)
data = res.json()

Response (abridged — this payload is large; see the API Reference for the full schema)

{
  "success": true,
  "data": {
    "heatmap": {
      "buckets": [
        { "weekday": 1, "hour": 9, "sent": 12, "replied": 5, "replyRate": 0.42 }
      ]
    },
    "topCampaigns": [
      { "campaignId": "campaign123", "name": "Spring Promo", "sent": 420, "replied": 180, "booked": 22, "replyRate": 0.43, "creditsSpent": 210.5 }
    ],
    "channelVolume": [
      { "channel": "whatsapp", "sent": 800, "received": 540, "lastMessageAt": "2026-05-31T14:02:11.000Z" }
    ],
    "inboxSla": { "medianFirstResponseMs": 92000, "sampleSize": 140 },
    "activityFeed": [
      { "id": "evt_1", "kind": "booked", "at": "2026-05-31T14:02:11.000Z", "contactId": "contact456", "contactName": "Jane Smith", "campaignId": "campaign123", "campaignName": "Spring Promo", "label": "Jane Smith booked an appointment" }
    ],
    "numberStats": null,
    "channelDailySeries": null,
    "metricDailyBreakdown": null,
    "contactsByCountry": null,
    "ai_human_split": null
  }
}

Field notes:

  • heatmap.buckets[].weekday0 is Sunday through 6 is Saturday.
  • numberStats, channelDailySeries, metricDailyBreakdown, contactsByCountry, ai_human_split — each independently null when the reporting database is unavailable for your account; every other block still returns.

Dashboard AI insights

Returns three short, AI-written insights about the account’s messaging over a date range: one win, one thing to watch, and one tip — sentences you can paste straight into a report rather than numbers you still have to interpret. Generated from the account’s own message metrics only.

GET /analytics/dashboard-ai-insights

Parameter Required Description
startDate Yes Range start, YYYY-MM-DD.
endDate Yes Range end, YYYY-MM-DD.

This endpoint is account-wide — it takes no campaign or agent scope.

cURL

curl "https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ startDate: "2026-05-01", endDate: "2026-05-31" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/dashboard-ai-insights",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"startDate": "2026-05-01", "endDate": "2026-05-31"},
)
data = res.json()

Response

{
  "success": true,
  "data": {
    "insights": [
      { "tone": "win", "title": "Reply rate is up", "detail": "Your reply rate climbed to 43% this period, up from 36% the period before." },
      { "tone": "watch", "title": "Bookings slowed midweek", "detail": "Wednesday bookings dropped to a third of Monday's, worth a look at your Wednesday follow-up timing." },
      { "tone": "tip", "title": "Re-send to non-repliers", "detail": "212 contacts received a message but never replied — a short follow-up template often recovers 10-15% of them." }
    ]
  }
}

Missing startDate or endDate returns 400.


Entity activity timeline

Returns a single contact’s, deal’s, or task’s activity as one timeline, newest first: what happened and when, across messages, appointments, notes, and status changes. Use it to answer “what has happened with this person” without stitching several list endpoints together.

GET /analytics/entity-activity

Parameter Required Description
entityType Yes contact, deal, or task.
entityId Yes ID of the record whose timeline to return.

cURL

curl "https://api.youraiconnector.com/v1/analytics/entity-activity?entityType=contact&entityId=contact456&apiKey=YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({ entityType: "contact", entityId: "contact456" });
const res = await fetch(`https://api.youraiconnector.com/v1/analytics/entity-activity?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/analytics/entity-activity",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"entityType": "contact", "entityId": "contact456"},
)
data = res.json()

Response

{
  "success": true,
  "data": {
    "items": [
      {
        "id": "evt_9",
        "kind": "appointment_booked",
        "at": "2026-05-31T14:02:11.000Z",
        "label": "Booked an appointment for June 3",
        "detail": "Consultation call, 30 minutes"
      },
      {
        "id": "evt_8",
        "kind": "message_replied",
        "at": "2026-05-31T13:58:02.000Z",
        "label": "Replied: \"Yes, that time works\""
      }
    ]
  }
}

A missing or invalid entityType/entityId returns 400. An entity that does not exist on your account returns 404, so other accounts’ ids stay unguessable.


Aggregated event counts (legacy)

Returns the same aggregated event counts as Message volume summary, but in the camelCase shape (contactCreated rather than contact_created, byDate rather than by_date) some older integrations were built against. Prefer /analytics/summary for new integrations — this endpoint exists only so the in-app dashboard and the API share one implementation.

GET /analytics/aggregate

Parameter Required Description
startDate No Range start, ISO date or date-time. Defaults to the same window /analytics/summary uses.
endDate No Range end, ISO date or date-time.
campaignId No Only count events belonging to this campaign (campaign_id also accepted). Legacy; prefer agent_id.
agent_id No Only count events belonging to this AI Agent (agentId also accepted).

cURL

curl "https://api.youraiconnector.com/v1/analytics/aggregate?startDate=2026-05-01&endDate=2026-05-31&apiKey=YOUR_API_KEY"

Response

{
  "success": true,
  "data": {
    "from": "2026-05-01",
    "to": "2026-05-31",
    "total": 1240,
    "byAnalyticType": {
      "total": 1240,
      "sent": 800,
      "delivered": 760,
      "read": 540,
      "replied": 210,
      "booked": 35,
      "contactCreated": 120,
      "creditsSpent": 412.5,
      "creditsRecharged": 500
    },
    "byDate": [
      { "date": "2026-05-01", "byAnalyticType": { "total": 40, "sent": 25, "delivered": 24, "read": 18, "replied": 7, "booked": 1, "contactCreated": 4, "creditsSpent": 13.5, "creditsRecharged": 0 } }
    ]
  }
}

Agency sub-account rollup


Analytics API errors

Analytics endpoints return the standard error envelope:

{
  "success": false,
  "error": "Date range too large. Maximum is 366 days."
}

On an analytics endpoint, an invalid date format or an out-of-range window returns 400, and an unknown campaign_id or agent_id returns 404. Sending both campaign_id and agent_id on an endpoint that accepts either is also a 400 — pass at most one. The PG-only reporting endpoints (Metric series, Conversation outcomes, Agency rollup) return 503 with "error_code": "analytics_unavailable" rather than a 200 full of zeros when the reporting database cannot answer for your account — retry shortly. The shared codes every endpoint can return — 401, 403 (your plan does not include API access, or, on the agency rollup, your account is not Agency/Dev), 429 (rate limit) and 500 — are listed with retry guidance in Errors & Pagination.


Next steps