VCurl — implementation brief for AI agents ============================================================ A quickstart for building against the VCurl API: the common calls, with copy-paste examples. For the FULL reference — every endpoint, field and response shape, plus update/delete, filters, pagination and more — read the OpenAPI spec: https://staging.vcurl.co/docs/api.json (browsable UI: https://staging.vcurl.co/docs/api) == GET A KEY (read first) == Bearer tokens. You (the agent) must NOT invent, guess or reuse a credential — ask your human to create a scoped API token in their dashboard (for a game, a one-click "submit key") and paste it back. Send it on every request: Authorization: Bearer Scope each token to what you need (request the narrowest set): links:* pastes:* countdowns:* files:* trackers:* bins:* emailbins:* (each :read :write :analytics/…) forms:read/write/submissions/submit · polls:read/write/votes/submit · games:read/write/submit availability:read/write/responses/submit gitconcierge:read/write/submit (support desks & tickets) · sendbrand:read/write (SendBrand brands) site-association:read/write · metrics:read mirrors:read/write · scrobbles:read (ScrobbleWith) · secretgifts:read/write (SecretGifts) [per-user — GLOBAL base] Check what a token can do: GET https://staging.vcurl.co/api/v1/token (any token, no ability needed) BASE URLS: global https://staging.vcurl.co/api/v1 · team-scoped https://.staging.vcurl.co/api/v1 is your team subdomain (from the dashboard); nearly every call below is team-scoped. == WHAT YOU CAN CREATE (HOST TYPES) == Each of these is a "host" — an object you create via the API and that the public then reaches through a slug (see the next section). Base paths are team-scoped (https://.staging.vcurl.co/api/v1): Short link /team/links a branded short URL that redirects to a destination (UTM + optional rule-based routing by geo/OS/time/…) Paste /team/pastes a titled markdown document, like a pastebin Countdown /team/countdowns a live page counting to (or up from) a moment: one date, a recurrence (daily-yearly, always next instance) or a list of dates (soonest upcoming); optional style + background File /team/files one hosted upload — image/video/PDF embeds on its page, anything else downloads Tracking pixel /team/trackers a pixel or hit-counter you embed in an email or page; every load counts Request bin /team/bins a throwaway URL that captures every HTTP request sent to it (inspect a webhook) Email bin /team/emailbins a throwaway inbox address (@) that captures every email sent to it Feedback form /team/forms a public form; responses collected privately for the team Poll /team/polls a public poll; votes collected, with an optional live tally Availability /team/availabilities a "when works?" poll; people pick time slots, best common time surfaced Git Concierge /team/support-desks a support desk backed by your git issues; open tickets via gitconcierge/{desk}/tickets Game /team/games umbrella for SCOREBOARDS (score tables) + ACHIEVEMENTS + daily STREAKS A game and each of its scoreboards are also slug hosts, so either can be published at a public URL. Separately, SITE ASSOCIATION (/team/site-associations) serves apple-app-site-association / assetlinks.json on a verified domain for mobile deep links — it's domain-level, not a slug. == HOW PUBLIC SLUGS WORK == A SLUG is the public URL that fronts a host — the ONLY public web surface. The public always reaches your object through its slug. - Creating a host mints its FIRST slug in the same call; add/remove more under .../{id}/slugs. One host can have MANY slugs (many public URLs pointing at the same object). - A slug lives on a domain: pass `domain` = a verified domain's UUID you own, or `null` for a shared public domain. The public URL is then https:/// - `path`: letters/numbers/-/_ ; omit it and a memorable name is generated (e.g. impulsive-pintail). - Per-slug guards: `password` (gate access), `expires_at` (stop resolving after a time), `max_hits` (retire after N hits). Hits count PER SLUG, so every URL is tracked separately. - Every slug has a scannable SVG QR at .../slugs/{slug}/qr; click-tracked hosts expose .../analytics. The direct https://.staging.vcurl.co/{type}/{uuid} page is a SIGNED-IN team-member preview (not public, not click-tracked); the slug is the public, shareable, tracked surface. == COMMON RECIPES == Each is a complete call. Public reads need no token; writes need the scope shown. # Create a short link (mints its first slug) curl -X POST https://.staging.vcurl.co/api/v1/team/links \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"destination_url":"https://example.com/very/long","path":"launch"}' # → {"data":{"id":"","slugs":[{"url":"https:///launch",...}], ...}} # Add another slug to an existing link curl -X POST https://.staging.vcurl.co/api/v1/team/links//slugs \ -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"path":"promo"}' # Create a paste (countdowns/files follow the same shape; files are multipart) curl -X POST https://.staging.vcurl.co/api/v1/team/pastes \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"title":"Release notes","content":"## v2\n\nShipped.","path":"notes"}' == GAMES — SCOREBOARDS, ACHIEVEMENTS & STREAKS == One GAME owns many SCOREBOARDS (score tables) plus shared ACHIEVEMENTS and daily STREAKS. {game} and {board} are UUIDs from the dashboard. Reads are public; writes need a games:submit key. # Read a scoreboard (public — no token) curl "https://.staging.vcurl.co/api/v1/scoreboards/?period=week&username=ACE" # List a game's achievements (public; add ?username= or ?private_identifier= for per-player progress) curl "https://.staging.vcurl.co/api/v1/games//achievements?username=ACE" # Submit a score curl -X POST https://.staging.vcurl.co/api/v1/scoreboards//entries \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"username":"ACE","score":128400,"private_identifier":"device-uuid"}' # score = points, or whole milliseconds on a time board. Returns the entry + its rank. # Unlock an achievement (manual achievements only; {key} e.g. first_win) curl -X POST https://.staging.vcurl.co/api/v1/games//achievements/first_win/unlock \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"username":"ACE","private_identifier":"device-uuid","amount":1}' # Check in a daily streak (idempotent per day; any score/unlock also counts as a check-in) curl -X POST https://.staging.vcurl.co/api/v1/games//streak \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"username":"ACE","private_identifier":"device-uuid"}' # GDPR-erase one player (BY IDENTIFIER ONLY — never username) curl -X DELETE https://.staging.vcurl.co/api/v1/games//player \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"private_identifier":"device-uuid"}' private_identifier is OPTIONAL, write-only, stored hashed — send it consistently so players sharing a username ("god") stay distinct, and so you can fetch/erase exactly one player. == FORMS & POLLS == Public form/poll pages live at their slug and humans post from the browser. To submit from your own client, use the scoped submit keys (embeddable, submit-only): # Create a feedback form curl -X POST https://.staging.vcurl.co/api/v1/team/forms \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"title":"Beta","path":"beta","fields":[{"key":"msg","label":"Message","type":"long_text","required":true}]}' # Submit a form response (forms:submit) — cast a poll vote is POST /polls//votes (polls:submit) curl -X POST https://.staging.vcurl.co/api/v1/forms//submissions \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"answers":{"msg":"Great tool"}}' # Read collected responses / votes with forms:submissions / polls:votes (team-scoped) — see the spec. == USER-SCOPED PRODUCTS: SCROBBLEWITH & SECRETGIFTS == These two are owned by YOU (the token's user), not a team, so they live on the GLOBAL base (https://staging.vcurl.co/api/v1 — there is NO subdomain for them). ScrobbleWith — mirror another Last.fm user's (or a radio station's) plays onto your own Last.fm profile, live. Needs a linked Last.fm account. (mirrors:read/write, scrobbles:read) # Start a mirror (POST extends via PATCH, stop via DELETE; read mirrored tracks at /tracks) curl -X POST https://staging.vcurl.co/api/v1/scrobble/mirrors \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"source_username":"rj","hours":24}' SecretGifts — run a Secret Santa / gift exchange: create it, invite people, draw & announce matches, then read the giver→receiver assignments. (secretgifts:read/write) # Create an exchange (seeds the 4 default questions) curl -X POST https://staging.vcurl.co/api/v1/secretgifts/exchanges \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"name":"Office Secret Santa","exchange_date":"2026-12-18","budget":20,"currency":"GBP"}' # Invite people (magic-link; existing addresses skipped), then publish and draw the matches curl -X POST https://staging.vcurl.co/api/v1/secretgifts/exchanges//participants \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"people":[{"email":"ann@example.com"},{"name":"Bo","email":"bo@example.com"}]}' curl -X POST https://staging.vcurl.co/api/v1/secretgifts/exchanges//match \ -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"announce":true}' == SENDBRAND — BRAND GUIDELINES AS A URL == A team publishes one or more BRANDS (colours, typography, logos, voice, social + links) as read-only pages on their own host, each with a machine-readable brief built for you. READ a brand with NO token, directly (permissive CORS) — the fastest way to hand a coding agent a brand: Markdown brief: https://.sendbrand.app//agent (Content-Type: text/markdown) Structured JSON: https://.sendbrand.app/.json (stable `schema_version`) Keyless by UUID: GET https://.staging.vcurl.co/api/v1/brands/ MANAGE brands over the team API (needs a sendbrand:read / sendbrand:write key): GET/POST https://.staging.vcurl.co/api/v1/team/brands · GET/PATCH/DELETE .../team/brands/ Create/patch takes the structured fields (name, slug, colours[{name,hex,dark?,group?,usage?}], type_styles, voice, socials[urls], links[{label,url}], related_brands[{brand_id,name?}], milestones[{date,title,description?}], awards[{date?,provider?,title,description?}], locations[{name,address?,city?,country?,url?}], contacts[{type:phone|email,label?,value}]). Logo FILES and key-people (with photos) are dashboard-managed, not writable via the API; they come back on read. Image URLs (logos, photos) are short-lived signed CDN links — DOWNLOAD the images and host your own copy; do NOT hotlink them. == RULES YOU MUST KNOW == PROFANITY: a name / title / label / tag / arcade username is REJECTED (422); a paste body and a countdown/file/game/scoreboard/achievement description is CENSORED to [CENSORED] ("censored":true). RATE LIMITS: public reads ~120/min; public WRITES (score/achievement/streak/form/poll submit) ~30/min PER PLAYER (by private_identifier, else IP), not per key. IMAGE URLS: an icon_url / avatar_url in a response is a signed CDN link that expires after ~24 hours. Fine to hold for a session; re-request the resource for a fresh URL if you cache longer than that. TRUST: a games:submit key ships in your client, so scores are spoofable — use score bounds (min_score/max_score), dashboard moderation and key rotation; validate server-side if it matters. WEBHOOKS (outbound): to be pushed events instead of polling, the team subscribes an HTTPS endpoint in the dashboard (Webhooks). There is NO API to configure endpoints — ask your human to add one and tick the events (bin/email/form/poll/score/achievement/click-milestone). You only build the receiver. METRICS: each team exposes Prometheus text at GET https://.staging.vcurl.co/metrics (needs a team token with metrics:read; at the host ROOT, not under the API base). The full metric list is in the spec. EXPORT: there is no API export — the account owner runs a full GDPR export from the dashboard. == EVERYTHING ELSE — READ THE SPEC == Update (PATCH) and delete (DELETE) for every object, list filters, pagination, QR codes, analytics rollups, bulk import, routing rules, custom domains, request bins, email bins, site association, and every request/response field are all documented — read it before building beyond these recipes: https://staging.vcurl.co/docs/api.json (browsable UI: https://staging.vcurl.co/docs/api)