curl -X POST https://api.useskate.com/v1/ask \
-H "Authorization: Bearer sk_skate_…" \
-H "Content-Type: application/json" \
-d '{"site": "s_…", "question": "How do I export invoices?", "user": "u_123"}'site: the site id (Settings → Sites, orlist_sitesover MCP).question: 3 to 2000 characters.user: optional, your own id for the person asking (up to 80 characters). It groups their questions in Conversations. Do not send an email address.
The key is an API key from Settings → API keys. A read-only key is enough.
The answer
{
"kind": "answer",
"answer": "Open Billing → Invoices and press Export CSV.",
"check": "grounded",
"answered": true,
"sources": [{ "kind": "instruction", "title": "Your instruction", "url": "", "text": "To export invoices, open Billing → Invoices and press Export CSV." }],
"task": "tsk_3f9a1c07b2de"
}kindanswer: an answer from your sources.link: the question asks for a change in the app.linkopens your app with the request ready, and Skate does it there with the person’s approval. The API itself never changes anything.person: one of your “a person handles this” instructions covers it, or the person asked for a human. No model is called and no credit is used.
checkgrounded: every number, price and date in the answer is in the sources it cites.ungrounded: the answer states a value its sources do not carry. Do not show it as fact.no_sources: it cites nothing.
answered:falsewhen your sources did not answer the question. The answer then says so.task: the id of this question in Conversations.
Skate never says it did, checked or looked up anything here: it has no page and cannot see the person’s account.
Cost and limits
- One task credit per question, like a widget task, and your daily AI limit applies. A
personanswer is free. - 30 questions per minute per key.
- Every question is logged as a conversation, so it counts in Topics, Support and the weekly report like a widget question. Its row carries
source: "api".
Errors
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_body, invalid_question | Not JSON, or a question shorter than 3 or longer than 2000 characters. |
| 401 | invalid_key | No key, a wrong key, or a revoked one. |
| 402 | out_of_credits | The account has no task credits left. |
| 404 | unknown_site | No such site on this account. |
| 429 | spend_cap, rate_limited | The daily AI limit is reached, or too many questions this minute (see Retry-After). |
| 503 | model_unavailable, unavailable | Skate could not answer just now. Try again. |
Banners, announcements, updates and surveys: /v1/items
Create and publish in-app content from your own code, for example a maintenance banner from your deploy pipeline. Use the same API key (Settings → API keys). Any key can read; changing needs a key made with write access.
# A draft banner
curl -X POST https://api.useskate.com/v1/items \
-H "Authorization: Bearer sk_skate_…" -H "Content-Type: application/json" \
-d '{"site":"s_…","kind":"banner","name":"Maintenance","content":{"body":"We are down on **Sunday** from 02:00 to 04:00 UTC.","cta":{"kind":"none"}}}'
# Publish it (use the id from the answer)
curl -X POST https://api.useskate.com/v1/items/bn_…/publish \
-H "Authorization: Bearer sk_skate_…" -H "Content-Type: application/json" -d '{"site":"s_…"}'| Call | What it does |
|---|---|
GET /v1/items?site=s_… | The site’s drafts and published items. Add &kind=banner (or modal, news, survey) to filter. |
GET /v1/items/<id>?site=s_… | One item, archived ones included. |
POST /v1/items | A new item, always a draft. Body: site, kind, name, content, and optionally style, display, survey, targeting, translations, variant. |
POST /v1/items/<id> | Change an item. Send only the fields you change; its state stays. |
POST /v1/items/<id>/publish | Visitors see it on their next page load. |
POST /v1/items/<id>/unpublish | Back to a draft. |
POST /v1/items/<id>/archive | Gone from the lists; can still be read by id. |
Items are checked by the same rules as the dashboard editor, so the API cannot make anything you could not make there (an https link, a readable colour, a valid audience). An answer carries the item as the editor sees it.
Limits: 60 calls in any 60 seconds per key, reads and writes alike (a sliding window, not a bucket that resets on the minute: the 61st call within 60 seconds of the first gets 429, and Retry-After says when the next one is allowed). 200 items per site. Every call is in the key’s audit log; every change (create, edit, publish, unpublish, archive) is also on the site’s Activity page, by “API key <name>”.
| Status | error | Meaning |
|---|---|---|
| 400 | invalid_body, invalid_kind, invalid_item, too_many | Not JSON, an unknown kind, an item the editor would refuse (errors says why), or 200 items already. |
| 401 | invalid_key | No key, a wrong key, or a revoked one. |
| 403 | read_only_key | The key can only read. |
| 404 | unknown_site, unknown_item, not_found | Not on this account, no such item, or no such route. |
| 429 | rate_limited | Too many calls this minute (see Retry-After). |