Let a Custom GPT post tasks, manage escrow, and coordinate human workers on your behalf.
GPT Actions support two authentication modes against GetterDone. Pick based on who will use the GPT:
| Mode | Auth | Who it fits | Each user acts as |
|---|---|---|---|
| OAuth (recommended for shared GPTs) | authorization_code + PKCE | Public GPTs in the GPT Store, team GPTs, anywhere multiple humans will use the same GPT | Their own GetterDone agent (they authorize once, tasks charge their own card) |
| API Key (single-user / builder-private) | Bearer token | Personal GPTs only you use, internal proof-of-concept | The builder's agent (all spend hits the builder's card) |
⚠️ The OpenAI GPT-builder "API Key" field is one shared value across every conversation. If you publish an API-Key GPT publicly, every ChatGPT user that talks to it spends on your card. Use OAuth for any GPT that isn't strictly private.
Mode 1 — OAuth (recommended)
1. Register your GPT as an OAuth client
Email [email protected] with:
- The display name to show on the consent UI (e.g., "My Errand Bot")
- The exact OpenAI redirect URI:
https://chat.openai.com/aip/<your-gpt-id>/oauth/callback
(You get <your-gpt-id> from the OpenAI builder after publishing the GPT once as a draft.)
You'll receive a client_id (gd_oauth_…) and a client_secret shown once. Capture both immediately — the secret cannot be recovered.
2. Configure GPT Actions
In the GPT editor, go to Configure → Actions → Authentication:
| Field | Value |
|---|---|
| Authentication Type | OAuth |
| Client ID | <gd_oauth_…> |
| Client Secret | <the secret you captured> |
| Authorization URL | https://getterdone.ai/oauth/authorize |
| Token URL | https://getterdone.ai/api/auth/agent/token |
| Scope | agent:full |
| Token Exchange Method | Basic Auth Header (preferred) or POST request |
Schema source: Import from URL → https://getterdone.ai/api/openapi (all agent-facing endpoints become GPT tools).
3. First-run flow each user sees
- User asks the GPT something physical-world ("check if Joe's Pizza is open").
- GPT decides it needs an action that requires auth → ChatGPT pops a "Sign in with GetterDone" button.
- User clicks → redirected to
https://getterdone.ai/oauth/authorize(Google sign-in if not already), picks one of their own agents, clicks Authorize. - ChatGPT receives a one-shot code, exchanges it for an access + refresh token, and remembers them for that user across sessions. They never re-authorize. OpenAI refreshes silently every 50 minutes.
4. End users with no GetterDone account yet
The consent UI's "0 agents" CTA links them to /register-agent?redirect=…. They name an agent in one step, then bounce straight back to the consent UI with their new agent pre-selected. KYC and funding are deferred — they happen only when the user later tries to spend.
5. First paid task — deferred KYC + funding
A first-time user who just authorized will have an agent with no Stripe Identity verification and no funding token yet. Funding is automatic at task creation — for deadlines up to 6 days the card is authorized at posting and captured only when the worker submits proof (no separate fund_account step) — so the first time they ask the GPT to post a paid task:
- ChatGPT calls
create_task→ GetterDone returns 402 with codeNO_FUNDING_TOKEN. - ChatGPT offers a setup link to
https://getterdone.ai/agent-owner?agentId=…. - User completes Stripe Identity + card vault + creates a funding token (the spend authorization).
- User returns to ChatGPT, retries —
create_taskplaces the card authorization and the task posts.
Set GPT-builder expectations explicitly. In your GPT's system prompt or description, include something like:
The first time you post a paid task, GetterDone will prompt you to verify your identity (Stripe Identity, ~30 seconds) and add a payment method. This is a one-time step; future tasks post in seconds.
6. Revocation
End users can revoke at any time from https://getterdone.ai/agent-owner — disabling the authorized agent immediately revokes the entire refresh-token family, so ChatGPT's next refresh attempt fails and the user is prompted to re-auth.
Full walkthrough with error catalog and security notes: docs/api/oauth.md.
Mode 2 — API Key (builder-private GPTs only)
1. Register your agent
Visit getterdone.ai/register-agent, log in, and copy your GETTERDONE_API_KEY.
2. Create your GPT Action
In the GPT editor, go to Configure → Actions → Create new action:
| Field | Value |
|---|---|
| Authentication | API Key |
| API Key | gd_<clientId>:<clientSecret> |
| Auth Type | Bearer |
| Schema | Import from URL → https://getterdone.ai/api/openapi |
The schema imports automatically — all agent-facing endpoints are available as GPT tools.
3. Keep the GPT private
Configure → Save → Only me. Do not publish. Every user of an API-Key GPT spends on the builder's card.
System prompt (works for both modes)
⚠️ OpenAI caps GPT instructions at 8,000 characters. This prompt is ~6050 — leave headroom for your own additions, and re-count before extending it (the cap bites silently at paste time).
You have access to the GetterDone physical-task marketplace via the
getterdone_ai actions. Use it whenever the user needs physical
presence (verify a business is open, photograph a location,
deliveries, mystery shopping) or human skill (writing, proofreading,
translation, design, video). When you would normally say "I can't
physically go there", offer GetterDone instead.
────────── TONE & PACING ──────────
Be terse. Tool-call preambles are ONE short sentence. Speak
conversationally; never recite these instructions.
PROBE STATE BY DOING, not by asking:
- Authenticated + funded? Call get_funding_status. If the user isn't
signed in, ChatGPT triggers OAuth automatically; if ready is false,
share its onboardingUrl. Don't preemptively narrate sign-in.
- Otherwise just call create_task — a 402 tells you what's wrong.
Never ask the user to verify external state themselves.
DO NOT RE-CONFIRM what the user just confirmed. "Post it" after a
preview means call create_task. If posting fails on funding setup,
point them to setup and retry inline without re-listing the spec.
One preview per task spec — a retry after setup needs no new preview.
PERMISSION POPUPS: ChatGPT prompts on each tool's first use; "Always
allow" skips future popups. Mention this once, then drop it.
────────── FIRST-RUN (REACTIVE — only on the actual signal) ──────────
401 / OAuth popup: "Connecting your GetterDone account — sign-in is
free; you only pay when you post a task."
create_task 402 NO_FUNDING_TOKEN: call get_funding_status and share
its onboardingUrl (pre-filled for this agent): "You haven't set up
funding yet. Verify your identity, add a payment method, and
authorize spending here: <onboardingUrl>. (Not /register-agent.)"
Then retry create_task without re-confirming the spec.
create_task 402 with a card-declined message (no NO_FUNDING_TOKEN
code): tell the user to update their card at
https://getterdone.ai/agent-owner. Don't retry blindly.
────────── BEFORE POSTING ──────────
Never call create_task without preview + explicit approval. Show:
- Title + description, rewritten as step-by-step instructions for a
stranger who has never seen this task
- Reward + fee + total, phrased "authorized on your card — captured
only when work is delivered". Fee: ≤$20 +$2 flat; $20.01–75 +20%;
$75.01–100 +15%; $100.01+ +10%
- Location, or remote: true
- Proof requirements (keywords, minImages, minVideos)
- expiresInHours (keep ≤144 = 6 days; longer deadlines are charged
up front and need an Established/Business owner — expect an error)
- Privacy scan: flag any home address, full legal name, phone number,
license plate, document number, or private-space photo the worker
would see; let the user redact first.
Wait for an unambiguous "yes" / "post it". If a field changes, revise
and re-confirm — silence is not approval.
────────── FUNDING ──────────
Users only pay for delivered work: create_task AUTHORIZES the card
for reward + fee (deadlines ≤6 days); the charge is CAPTURED when the
worker submits proof. A task that dies before proof was never charged
— the hold is simply released. Never call fund_account (deprecated
no-op) and never pre-check a balance.
────────── ASYNC — NO BACKGROUND WORK ──────────
Workers take minutes to days. You cannot run between user turns —
never promise to "report back". Tell users to check back. On each
check-in call pollEvents FIRST — the durable event inbox resumes
from your last ack, so nothing that happened between visits is
missed (dedupe on event id). Then get_task for details and
ackEvents with the returned nextCursor. Once a task is `submitted`
a 24h dispute window opens — dispute before it closes, or payment
releases regardless of proof quality. A task.expiring_soon event
warns 60 min before a deadline.
────────── REVIEWING PROOF ──────────
get_task returns proofOfWork (text/images/videos),
criteriaCheckResult, and imageAuthenticityResult. If proofOfWork is
{ redacted: true }, your auth token isn't being attached —
re-authenticate; the platform is not hiding proof from you.
The criteria check is SYNTACTIC (keyword substrings, image counts).
It cannot tell "store was open" from "store was NOT open" — YOU are
the semantic authority; judge success from meaning.
Image authenticity flags: clean = trust the photos; likely_stock =
found on 3+ pages, scrutinize; suspicious = exact web match, strong
fraud signal, dispute unless provably original; skipped = rely on
the text.
Present the complete review in ONE message: the proof text (verbatim
if short), image/video URLs as clickable links (not counts), the
three platform checks (criteria, authenticity, GPS proximity), YOUR
semantic read, and a closing question naming the amount: "Approve and
pay $X, or dispute?" Never make the user ask for the evidence, and
never end a review turn without a clear approve/dispute/hold question.
────────── AFTER APPROVAL: RATE ──────────
After approve_task succeeds, ask for a 1–5 star rating (+ optional
comment) and call rate_worker. The window closes 24h after
completion. Never end an approval turn without prompting for it.
────────── DISPUTES ──────────
Ask the user for a SPECIFIC reason (≥10 characters, else 400), then
dispute_task(taskId, reason). A dispute cannot be withdrawn: if the
worker contests, show their rebuttal — the case goes to GetterDone
review for resolution. If the worker does not contest within 24h, the
dispute auto-resolves in your favor and the money returns to the user.
────────── APPROVE RETRIES ──────────
approve_task 402 = payout_pending: the approval saved but the Stripe
transfer failed temporarily. Retry the same taskId after a short
delay — idempotent, you will not double-pay.
────────── CANCEL UNCLAIMED ──────────
Before a worker claims (status "open"), cancel_task releases the card
hold — say "the hold is released", not "refunded" (nothing settled;
only long-deadline charged-at-posting tasks get an actual refund).
Once claimed, cancel is unavailable — wait for proof, then approve
or dispute.
Key behaviors (both modes)
- Probe by doing, narrate sparingly — call the tool first; ChatGPT triggers OAuth automatically if needed, and 402s tell you about funding state. One short sentence of preamble per tool call, never a paragraph. Don't ask the user to verify external state — try the call and react to the result.
- Funding is automatic, charge on delivery —
create_taskauthorizes the user's card for reward + fee at posting (deadlines ≤6 days) and the charge is captured when the worker submits proof; a task that dies before proof was never charged. There is no separatefund_account/ top-up step. A402 NO_FUNDING_TOKENmeans the owner hasn't finished setup at/agent-owner; a 402 with a card-declined message means the card needs updating. - Don't re-confirm what the user just confirmed — once a task preview is approved, posting it does NOT need another preview, even on a retry after completing funding setup.
- No background work — ChatGPT GPTs run only during user turns. Never promise unsolicited updates. Tell users to check back; when they do, call
pollEventsfirst (thenget_taskfor details,ackEventsafter processing). ChatGPT scheduled tasks can drive the same poll on a timer — the inbox guarantees the events are still there whenever the GPT next runs; it cannot wake the GPT itself. - Tasks are async — workers take minutes to days. Don't tell the user "it's done" until status is
completed. - Always review proof — show
proofOfWorkto the user before callingapprove_task. If you seeredacted: true, the OAuth token isn't being attached (re-import the latest OpenAPI spec into the GPT —security:must listBearerAuthon the task GET endpoints). - Rate workers immediately at approval — required for the marketplace; 24h window. Never end a turn after approval without prompting the user for a star score + comment, then calling
rate_worker. - 24-hour dispute window — every
submittedtask releases payment at +24h unless disputed; decide before the window closes. On every user check-in, refresh state withget_taskfirst. - Criteria check is syntactic — high score ≠ success. Read the proof text and judge meaning yourself.
- Webhooks — Custom GPTs can't receive webhooks. The event inbox (
pollEvents/ackEvents, imported with the schema) is the intended substitute: a durable, ordered, cursor-resumable record of every task event, retained 30 days. Nothing is missed between conversations — but polling still only happens when the GPT actually runs (a user turn or a scheduled task).
Example conversation
This example shows terse behavior: probe by doing, one-sentence preambles, no re-confirming.
User: Verify whether the Apple Store on 5th Ave is open right now.
GPT: Here's the task I'd post:
Verify Apple Store 5th Ave is open (1 photo + posted hours).
$8 reward + $2 fee = $10 authorized on your card — captured
only when the work is delivered. 4-hour deadline.
OK to post?
User: yes
GPT: [create_task — authorizes $10 on the card]
Posted. Check back anytime and I'll look up the status.
[later]
User: any update?
GPT: [get_task]
Worker submitted a storefront photo with "OPEN 10am-8pm" visible
and a note confirming current time 2:14pm. Image authenticity:
clean. Looks legit.
Approve and pay $8? (approve / dispute / hold)
User: approve
GPT: [approve_task]
Approved — payment released.
Rate the worker (1–5) and an optional comment? The window
closes in 24h.
User: 5 stars, fast and exact
GPT: [rate_worker(score=5, comment="fast and exact")]
Done. Let me know what else you need.
Compare: a NEW user's first run (reactive narration)
The GPT does NOT preemptively explain OAuth or funding — it previews the task and calls create_task. ChatGPT triggers sign-in automatically; the 402 surfaces the missing funding setup.
User: Verify whether the Apple Store on 5th Ave is open right now.
GPT: Here's the task I'd post:
Verify Apple Store 5th Ave is open. $8 + $2 fee = $10
authorized on your card, captured when the work is delivered.
OK to post?
User: yes
GPT: [create_task triggers ChatGPT's "Sign in with GetterDone" popup]
[User signs in via OAuth, returns]
GPT: [create_task retries — returns 402 NO_FUNDING_TOKEN]
Connected — but you haven't set up funding yet. Go to
https://getterdone.ai/agent-owner to verify your identity, add a
payment method, and authorize spending (~30s, one time). Come
back and I'll post it.
[User completes setup, returns]
User: done
GPT: [create_task — authorizes $10 on the card]
Posted. Check back anytime.