← Community Desk / API
Tokens

Drive Community Desk from your own code

The page does its free work in the browser: it reads what your product is and your notes on one community (labelled lines or prose), works out the member band and the owner's phase role, the skill's five health metrics (DAU/MAU ratio, new member post rate, thread reply rate, lurker ratio, content created by non-staff), the top-5 share of posts and the time to a first reply, with flags CM-01..CM-13 for the warning signs and the gaps. Its two paid lanes are available over HTTP. You send one JSON object with task set to "audit" or "plan", and the model replies with one JSON object. The easiest way to build the facts string is the page itself: CommunityKit.buildInput(lane, CommunityKit.analyze(you, community, {company, platform, goal, question}), {auditHandoff}) in commkit.js, which also runs under Node.

Base URL: https://api.skillsafe.ai/v1/app-api. Every call sends Content-Type: application/json and, once you have a token, Authorization: Bearer <token>. Both lanes run on gpt-terra with a 10% publisher markup. A run needs a signed-in (user) token and is metered: the platform holds the estimate's hold_credits while the run is in flight and then charges only what it used (charged_credits). /estimate is free. A plain-text summary is at /llms.txt.

The response envelope

Every response is {"data": ..., "error": null} on success or {"data": null, "error": {"code", "message"}} on failure.

Error codes

HTTPcodemeaning
400VALIDATION_ERRORThe body is not a JSON object, or a field is the wrong type. Every field of the run input is a string.
401UNAUTHORIZEDNo token, or an expired one: get a new one (step 2).
402INSUFFICIENT_CREDITSThe balance is below the run's minimum. Top up at skillsafe.ai/account/billing/.
403FORBIDDENA guest token tried a metered run: sign in for a personal token.
404NOT_FOUNDUnknown job id.
429RATE_LIMITEDBack off and retry with the same Idempotency-Key.

The task field: two lanes

taskwhat the model does
"audit"The skill's health audit report: health of healthy, at risk, unhealthy or pre-launch (healthy is not allowed while the page raised a warning flag; two or more high flags mean unhealthy), the stage and phase_role, the community model, the shared identity, the metrics_reading, where members drop off (leak), the four warning_signs in order and exactly three priorities, each tied to a health metric.
"plan"The work: the weekly core_loop, a pinned welcome_post (at most 120 words), a welcome_dm (at most 80 words), first_week prompts, 2 to 5 rituals with at least one weekly, the power_users program with a personal ask (at most 80 words), close_the_loop and 3 to 5 health metrics.

Input fields (all strings)

fieldwhencontent
taskrequiredaudit or plan
companyrequiredYour product or brand name (may be empty).
platformrequireddiscord, slack, circle, reddit, facebook, forum or other.
goaloptionalThe one goal that matters: retention, activation, word-of-mouth, support deflection, product feedback or revenue.
yourequiredWhat your product is: who it is for, who the ideal member is, who runs the community and for how long. Past 5,000 characters the page cuts the middle on line boundaries and leaves a [community-desk: N characters cut from the middle] marker.
communityrequiredYour notes on the community: members, daily and monthly active, new members, posts, replies, team posts, top-5 posts, time to first reply, channels, rituals. Cut the same way past 6,000 characters.
factsrequiredA JSON string: company, platform, goal, today, community{name, launched, members, dau, wau, mau, posters30, new_members, new_posters, posts, replied, staff_posts, top5, lurkers, unanswered}, stage, phase{name, role}, you{member_stated, member_quote, hours_per_week}, metrics{dau_mau, new_post_rate, reply_rate, lurker_ratio, nonstaff_share, staff_share, top5_share, reply_hours}, mentions{journey, rituals, power_users, intro_stop}, flags[{id, severity, text}], notes[].
questionoptionalWhat you want to know.
audit_handoffplan onlyA JSON string of the audit the plan follows: health, stage, leak, priorities[].
retry_noteoptionalOnly on a reformat retry: what was wrong with the previous reply.

The reply contracts

Both lanes share an envelope: task, headline, questions[], summary.

task: "audit"

health: healthy|at risk|unhealthy|pre-launch, reason, stage: pre-launch|0-100|100-1,000|1,000+, phase_role, model: support-driven|product-development|education/enablement|founder-led|blend, model_why, identity{statement, evidence[]}, metrics_reading, leak{where, why, evidence[]}, warning_signs[{sign, shown: yes|no|unknown, evidence[]}] (exactly four, in order), priorities[{fix, why, first_step, metric}] (exactly three), find_out[].

task: "plan"

source (audit handoff|from the input), goal, phase_role, core_loop, welcome_post{title, body}, welcome_dm, first_week[], rituals[{name, cadence: weekly|monthly|seasonal, owner, what}], power_users{who, offer[], ask}, close_the_loop, metrics[{metric, target}]. Metric names: DAU/MAU ratio, new member post rate, thread reply rate, lurker ratio, content created by non-staff.

Worked examples

These are the page's own bundled examples (invented companies, people and numbers), with the input as app.js builds it and the saved model reply the page replays for free. One worked example per health call and per lane, and the audit-to-plan handoff.

Audit: a 2,400-member Discord where the team writes most posts and questions wait 31 hours (unhealthy)

Input (description, notes and facts shortened here):

{
 "task": "audit",
 "company": "Tallyhook",
 "platform": "discord",
 "you": "Tallyhook - an open-source feature-flag service for product engineering teams.\nOur ideal member is a backend or platform engineer at a 20-200 person startup who runs flags in production.\nGoal for the community: fewer support tickets; most questions today are about SDK setup.\nOne developer advocate (Odell Varrick) spends about 6 hours a week in the Discord.\n",
 "community": "Tallyhook Discord\nLaunched: January 2025\nMembers: 2,400\nMonthly active: 610\nDaily active: 70\nMembers who posted in the last 30 days: 230\nNew members (last 30 days): 180\nNew members who posted within 7 days: 14\nPosts (last 30 days): 520\nPosts with at least one reply: 290\nPosts by our team: 310\nMedian time to first reply: 31 hours\nUnanswered questions (over 24 hours): 46\nMost new people post an intro in #introductions ... (535 characters in all)",
 "facts": "(a JSON string; decoded: company Tallyhook, platform discord, goal support deflection, community Tallyhook Discord, 2400 members, stage 1,000+ (Community Enabler), DAU/MAU 11.5, new member post rate 7.8, thread reply rate 55.8, lurker ratio 90.4, non-staff content 40.4, top-5 share None, first reply 31 hours; flags CM-01, CM-02, CM-04, CM-05, CM-10, CM-11)",
 "goal": "support deflection",
 "question": "Why is nobody but us answering?"
}

Reply:

{
 "task": "audit",
 "health": "unhealthy",
 "reason": "Two high flags fire together: the team writes most of the posts (CM-01) and the first reply takes 31 hours, with 46 questions unanswered past 24 hours (CM-02).",
 "stage": "1,000+",
 "phase_role": "Community Enabler",
 "model": "support-driven",
 "model_why": "The goal is support deflection and most questions are about SDK setup, which the skill pairs with a support-driven community.",
 "identity": {
  "statement": "Backend and platform engineers at startups who run feature flags in production and want to ship safely.",
  "evidence": [
   "backend or platform engineer at a 20-200 person startup who runs flags in production"
  ]
 },
 "metrics_reading": "DAU/MAU is 11.5%, under the 20% the skill calls healthy, the new member post rate is 7.8%, the thread reply rate is 55.8%, the lurker ratio is 90.4% and content created by non-staff is 40.4%. The median first reply of 31 hours is past the skill's 24-hour line.",
 "leak": {
  "where": "posting once and disappearing",
  "why": "New people stop at their intro, so almost nobody stays long enough to become someone who answers an SDK question, which leaves the team doing the answering.",
  "evidence": [
   "Most new people post an intro in #introductions and never post again."
  ]
 },
 "warning_signs": [
  {
   "sign": "most posts are from the company team",
   "shown": "yes",
   "evidence": [
    "Posts by our team: 310"
   ]
  },
  {
   "sign": "questions unanswered for more than 24 hours",
   "shown": "yes",
   "evidence": [
    "Median time to first reply: 31 hours",
    "Unanswered questions (over 24 hours): 46"
   ]
  },
  {
   "sign": "the same 5 people account for 80%+ of engagement",
   "shown": "unknown",
   "evidence": []
  },
  {
   "sign": "new members stop po
... (shortened here; the page shows it whole)

Audit: a 640-member customer Slack where five veterans write most of the posts (at risk)

Input:

{
 "task": "audit",
 "company": "Haulquill",
 "platform": "slack",
 "you": "Haulquill - workflow software for operations managers at logistics companies (50-1,000 employees).\nOur members are ops managers and dispatch leads who want to run calmer operations and grow into director roles.\nWe want the community to keep customers around: renewals are the number we care about.\nCommunity manager: Brisa Quennel, about 10 hours a week.\n",
 "community": "Ops Floor - our customer Slack community\nMembers: 640\nMonthly active: 210\nDaily active: 52\nNew members last 30 days: 48\nNew members who posted within 7 days: 21\nPosts last 30 days: 380\nPosts with at least one reply: 330\nPosts by our team: 70\nPosts by the top 5 members: 310\nMedian time to first reply: 3 hours\nWe run a weekly \"What broke this week?\" thread every Monday.\nNew members get a welcome DM from Brisa and a pin... (527 characters in all)",
 "facts": "(a JSON string; decoded: company Haulquill, platform slack, goal retention, community Ops Floor, 640 members, stage 100-1,000 (Community Manager), DAU/MAU 24.8, new member post rate 43.8, thread reply rate 86.8, lurker ratio None, non-staff content 81.6, top-5 share 81.6, first reply 3 hours; flags CM-03, CM-11)",
 "goal": "retention"
}

Reply:

{
 "task": "audit",
 "health": "at risk",
 "reason": "Most metrics clear the skill's lines, but the five most active people make 81.6% of the posts (CM-03), so the community depends on a few veterans.",
 "stage": "100-1,000",
 "phase_role": "Community Manager",
 "model": "education/enablement",
 "model_why": "The goal is retention, which the skill pairs with an education and enablement community that helps ops managers get better at their jobs.",
 "identity": {
  "statement": "Ops managers and dispatch leads at logistics companies who want calmer operations and a path to a director role.",
  "evidence": [
   "ops managers and dispatch leads who want to run calmer operations and grow into director roles"
  ]
 },
 "metrics_reading": "DAU/MAU is 24.8%, above the skill's 20% line, the new member post rate is 43.8%, the thread reply rate is 86.8% with a 3-hour median first reply, and content created by non-staff is 81.6%. The lurker ratio cannot be computed: a line like \"Members who posted in the last 30 days\" would let the page read it.",
 "leak": {
  "where": "a few voices",
  "why": "The top 5 members wrote 310 of 380 posts, so if a veteran steps back, answers and activity drop with them.",
  "evidence": [
   "The same few veterans answer almost everything",
   "Posts by the top 5 members: 310"
  ]
 },
 "warning_signs": [
  {
   "sign": "most posts are from the company team",
   "shown": "no",
   "evidence": []
  },
  {
   "sign": "questions unanswered for more than 24 hours",
   "shown": "no",
   "evidence": []
  },
  {
   "sign": "the same 5 people account for 80%+ of engagement",
   "shown": "yes",
   "evidence": [
    "Posts by the top 5 members: 310",
    "The same few veterans answer almost everything"
   ]
  },
  {
   "sign": "new members stop posting after their
... (shortened here; the page shows it whole)

Plan: the same Slack, carrying the audit

Input (with the audit handed over):

{
 "task": "plan",
 "company": "Haulquill",
 "platform": "slack",
 "you": "Haulquill - workflow software for operations managers at logistics companies (50-1,000 employees).\nOur members are ops managers and dispatch leads who want to run calmer operations and grow into director roles.\nWe want the community to keep customers around: renewals are the number we care about.\nCommunity manager: Brisa Quennel, about 10 hours a week.\n",
 "community": "Ops Floor - our customer Slack community\nMembers: 640\nMonthly active: 210\nDaily active: 52\nNew members last 30 days: 48\nNew members who posted within 7 days: 21\nPosts last 30 days: 380\nPosts with at least one reply: 330\nPosts by our team: 70\nPosts by the top 5 members: 310\nMedian time to first reply: 3 hours\nWe run a weekly \"What broke this week?\" thread every Monday.\nNew members get a welcome DM from Brisa and a pin... (527 characters in all)",
 "facts": "(a JSON string; decoded: company Haulquill, platform slack, goal retention, community Ops Floor, 640 members, stage 100-1,000 (Community Manager), DAU/MAU 24.8, new member post rate 43.8, thread reply rate 86.8, lurker ratio None, non-staff content 81.6, top-5 share 81.6, first reply 3 hours; flags CM-03, CM-11)",
 "goal": "retention",
 "audit_handoff": "{\"health\":\"at risk\",\"stage\":\"100-1,000\",\"leak\":\"a few voices\",\"priorities\":[\"Turn the veterans into a named advocate group with public recognition, and ask each one to bring in and coach a newer member as a second answerer.\",\"Use the Monday \\\"What broke this week?\\\" thread to pull newer members into answering, by taggi..."
}

Reply:

{
 "task": "plan",
 "source": "audit handoff",
 "goal": "retention",
 "phase_role": "Community Manager",
 "core_loop": "Every week each member answers or adds to one problem in the Monday \"What broke this week?\" thread, and Brisa Quennel credits the most useful answers by name in a Friday shoutout.",
 "welcome_post": {
  "title": "Start here: welcome to Ops Floor",
  "body": "Welcome to Ops Floor, the room for ops managers and dispatch leads who want calmer operations and a path to a director role.\n\nGreat participation here is practical: share what broke, how you fixed it, and what you would do differently. Nobody needs to be an expert. A newer voice on a familiar problem is often the most useful reply.\n\nStart here:\n- Say hello in [channel] with your role and the operation you run.\n- Answer one problem in Monday's \"What broke this week?\" thread, even with a partial fix.\n- Join the monthly career session hosted by one of our veterans.\n\nOur veteran advocates are here to help you find your footing. Brisa"
 },
 "welcome_dm": "Hi [Name],\n\nBrisa Quennel here, I run Ops Floor. Welcome in.\n\nOne easy question: what is the one thing that broke in your operation recently that you would love a second opinion on?\n\nPost it in Monday's \"What broke this week?\" thread here: [link]. Our veterans and other dispatch leads reply there first.\n\nBrisa",
 "first_week": [
  "Reply to one problem in Monday's \"What broke this week?\" thread where Brisa has tagged you because it matches your experience.",
  "Share one dispatch or scheduling habit that made a shift calmer, in [channel].",
  "Ask the veteran advocate who welcomed you one question about the problem you are working on, in a public thread."
 ],
 "rituals": [
  {
   "name": "What broke this week?",
   "cadence": "
... (shortened here; the page shows it whole)

Audit: a 3,100-member forum with a weekly thread, a monthly showcase and badges (healthy)

Input:

{
 "task": "audit",
 "company": "Cairnfeld",
 "platform": "forum",
 "you": "Cairnfeld - mapping software for trail builders and land managers.\nOur members are trail designers, crew leads and park staff who see themselves as stewards of public land.\nWe want word-of-mouth: most of our customers today found us through another trail builder.\n",
 "community": "Cairnfeld Trailcraft Forum (Discourse)\nMembers: 3,100\nMonthly active: 980\nDaily active: 260\nNew members last 30 days: 140\nNew members who posted within 7 days: 61\nPosts last 30 days: 1,450\nPosts with at least one reply: 1,320\nPosts by our team: 160\nPosts by the top 5 members: 240\nMedian time to first reply: 4 hours\nNew members land on a pinned \"Start here\" topic and introduce themselves in #trailheads.\nEvery month we... (604 characters in all)",
 "facts": "(a JSON string; decoded: company Cairnfeld, platform forum, goal word-of-mouth, community Cairnfeld Trailcraft Forum, 3100 members, stage 1,000+ (Community Enabler), DAU/MAU 26.5, new member post rate 43.6, thread reply rate 91, lurker ratio None, non-staff content 89, top-5 share 16.6, first reply 4 hours; flags none)",
 "goal": "word-of-mouth",
 "question": "What should we do next?"
}

Reply:

{
 "task": "audit",
 "health": "healthy",
 "reason": "No warning flag fires: DAU/MAU is above 20%, the team writes only 11% of posts, first replies come in 4 hours and the top 5 members make 16.6% of posts.",
 "stage": "1,000+",
 "phase_role": "Community Enabler",
 "model": "founder-led",
 "model_why": "The goal is word-of-mouth, which the skill pairs with a founder-led community, and most customers already arrive through another trail builder.",
 "identity": {
  "statement": "Trail designers, crew leads and park staff who see themselves as stewards of public land.",
  "evidence": [
   "trail designers, crew leads and park staff who see themselves as stewards of public land"
  ]
 },
 "metrics_reading": "DAU/MAU is 26.5%, above the skill's 20% line, the thread reply rate is 91% with a 4-hour median first reply, content created by non-staff is 89% and the new member post rate is 43.6%. The lurker ratio cannot be computed: a line like \"Members who posted in the last 30 days\" would let the page read it.",
 "leak": {
  "where": "joining but not posting",
  "why": "Every other metric is strong, but only 43.6% of new members post within 7 days of joining, which is where the flywheel loses the most people.",
  "evidence": [
   "New members who posted within 7 days: 61",
   "New members last 30 days: 140"
  ]
 },
 "warning_signs": [
  {
   "sign": "most posts are from the company team",
   "shown": "no",
   "evidence": []
  },
  {
   "sign": "questions unanswered for more than 24 hours",
   "shown": "no",
   "evidence": []
  },
  {
   "sign": "the same 5 people account for 80%+ of engagement",
   "shown": "no",
   "evidence": []
  },
  {
   "sign": "new members stop posting after their intro",
   "shown": "unknown",
   "evidence": []
  }
 ],
 "priorities": [
  {
   "fix": "Clo
... (shortened here; the page shows it whole)

Plan: a community that has not launched yet, from the input

Input:

{
 "task": "plan",
 "company": "Glazewick",
 "platform": "circle",
 "you": "Glazewick - booking and class-management software for independent ceramics studios.\nBuilt for studio owners who teach wheel-throwing classes and want full classes without spending their evenings on admin.\nOur founder, Liesl Tharnby, will run the community herself for the first months, about 4 hours a week.\nWe have 60 paying studios and a handful of them already recommend us to other studios.\n",
 "community": "Glazewick Studio Owners Circle - not launched yet\nPlatform: Circle\nLaunch: we want to open it in early 2027\nMembers: 0\nPlan so far: invite our happiest studio owners first, then open it to every customer.\nIdeas: a space to share class formats and pricing, and a monthly show-and-tell of student work.\n",
 "facts": "(a JSON string; decoded: company Glazewick, platform circle, goal word-of-mouth, community Glazewick Studio Owners Circle, 0 members, stage pre-launch (Community Architect), DAU/MAU None, new member post rate None, thread reply rate None, lurker ratio None, non-staff content None, top-5 share None, first reply None hours; flags CM-13)",
 "goal": "word-of-mouth"
}

Reply:

{
 "task": "plan",
 "source": "from the input",
 "goal": "word-of-mouth",
 "phase_role": "Community Architect",
 "core_loop": "Every week each studio owner shares one class format, price or full-class win, and Liesl Tharnby features the most useful posts by name in her weekly roundup.",
 "welcome_post": {
  "title": "Start here: welcome to the Glazewick Studio Owners Circle",
  "body": "Welcome. This Circle is for independent ceramics studio owners who teach wheel-throwing and want full classes without losing their evenings to admin.\n\nGreat participation here looks like this: you share what actually worked, a class format, a price change, a waitlist trick, and you ask the question you would ask a studio owner friend over coffee.\n\nStart here:\n- Introduce yourself in [channel]: your studio, your city and the class you are proudest of.\n- Post one class format or pricing question in Class Formats and Pricing.\n- Bring a photo of student work to the monthly show-and-tell.\n\nI read every post. Liesl Tharnby"
 },
 "welcome_dm": "Hi [Name],\n\nLiesl Tharnby here, founder of Glazewick. I am so glad your studio is in the Circle.\n\nOne easy question: what is the wheel-throwing class that fills fastest at your studio, and why do you think it works?\n\nShare it in Class Formats and Pricing here: [link]. Other owners are already asking about exactly this.\n\nLiesl Tharnby",
 "first_week": [
  "Seed post from Liesl Tharnby: \"Our best-selling class format, and what we would change\", with a reply prompt asking each owner to post theirs.",
  "Seed post: \"How do you price a drop-in wheel class versus a multi-week course?\" with Liesl Tharnby tagging founding members by name to answer.",
  "Seed post: \"Show us one piece a student made this month\", as a warm-up for the monthly 
... (shortened here; the page shows it whole)

The page re-checks the reply; the API does not

The page holds every reply to account (recon.js): for an audit, pre-launch exactly when the page read no members yet; healthy only when the page raised none of the warning flags; unhealthy when two or more high flags are raised; the stage and phase role the page computed; the community model the goal points to; the identity and the leak quoted; the four warning signs in order, yes wherever the page flagged one; exactly three priorities, each tied to a health metric. For a plan, the goal you chose; a welcome post of at most 120 words; a welcome message and a power-user ask of at most 80 words; 2 to 5 rituals with at least one weekly; offers from the skill's list; 3 to 5 health metrics, each once, with no invented target. In both, quotes are checked word for word and no count, rate, percentage or duration may be invented. Over the API you get the model's reply as it is; run the same checks yourself with Recon.reconcile in recon.js, or paste the same input into the page.

Step 1 — a tiny client helper

One function that sends JSON and unwraps the envelope.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="paste-your-token-here"   # from https://community-desk.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"community-desk"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself (not wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model reads gpt-5.6-terra and model_alias gpt-terra.

# input.json: the run input object - {"task": "audit", "company": "...", "platform": "discord", "goal": "...", "you": "...", "community": "...", "facts": "..."}
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses community-desk:<lane>:<inputHash>:a<attempt>. The job's output.output is the reply text; truncated: true means the balance cut it short.

KEY="community-desk:audit:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the lane's contract
echo "$J" | jq '.data | {charged_credits, truncated}' 

Step 6 — POST /run-stream (plan)

The same run as Server-Sent Events. plan.json is a plan-lane input, with audit_handoff when it follows an audit.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: community-desk:plan:$(shasum -a 256 plan.json | cut -c1-16):a1" --data @plan.json