{"openapi":"3.0.3","info":{"title":"The Focus API","description":"Identity, credits, job tracking for thefocus.ai apps","version":"0.1.0"},"servers":[{"url":"https://account.thefocus.ai"},{"url":"http://localhost:3000"}],"tags":[{"name":"Auth"},{"name":"Wallet"},{"name":"Jobs"},{"name":"X Integration"},{"name":"OAuth Tokens"},{"name":"Device"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"twitterBearer":{"type":"http","scheme":"bearer"}},"schemas":{"Account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"clerk_id":{"type":"string"},"plan":{"type":"string"},"credits":{"type":"integer"}},"required":["id","clerk_id","plan","credits"]},"AppEntry":{"type":"object","properties":{"slug":{"type":"string"},"starter_credits":{"type":"integer"},"granted":{"type":"boolean"},"granted_at":{"type":"string","nullable":true}},"required":["slug","starter_credits","granted"]},"WalletResponse":{"type":"object","properties":{"account":{"$ref":"#/components/schemas/Account"},"apps":{"type":"array","items":{"$ref":"#/components/schemas/AppEntry"}},"app_grant":{"type":"object","properties":{"slug":{"type":"string"},"starter_credits":{"type":"integer"},"granted":{"type":"boolean"}},"nullable":true}},"required":["account","apps"]},"JobStartRequest":{"type":"object","properties":{"app":{"type":"string"},"action":{"type":"string"},"cost_estimate":{"type":"integer","minimum":0},"metadata":{"type":"object","additionalProperties":true}},"required":["app","action","cost_estimate"]},"JobStartResponse":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"}},"required":["job_id"]},"JobCompleteRequest":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"actual_cost":{"type":"integer","minimum":0},"metadata":{"type":"object","additionalProperties":true}},"required":["job_id","actual_cost"]},"JobFailRequest":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"refund":{"type":"boolean","default":true}},"required":["job_id"]},"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"SessionUnauthorized":{"type":"object","properties":{"error":{"type":"string"},"signin_url":{"type":"string","description":"URL to initiate sign-in with redirect_to back to caller"}},"required":["error","signin_url"]}}},"security":[{"bearerAuth":[]}],"paths":{"/api/device/start":{"post":{"tags":["Device"],"summary":"Start device-code login","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string"},"user_code":{"type":"string"},"verification_uri":{"type":"string"},"expires_in":{"type":"integer"},"interval":{"type":"integer"}},"required":["device_code","user_code","verification_uri","expires_in","interval"]}}}}}}},"/api/device/complete":{"post":{"tags":["Device"],"summary":"Complete device login (browser, requires Clerk session)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_code":{"type":"string"}},"required":["user_code"]}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Invalid code"},"401":{"description":"Unauthorized"},"410":{"description":"Expired or already used"}}}},"/api/device/poll":{"get":{"tags":["Device"],"summary":"Poll for device token","parameters":[{"name":"device_code","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Token ready","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"202":{"description":"Pending"},"404":{"description":"Not found"},"410":{"description":"Expired"}}}},"/api/wallet":{"get":{"tags":["Wallet"],"summary":"Get wallet and app grants","parameters":[{"name":"app","in":"query","schema":{"type":"string"},"required":false,"description":"App slug to upsert and possibly grant starter credits"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs":{"get":{"tags":["Jobs"],"summary":"List user's jobs with optional filters","parameters":[{"name":"app","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by app slug"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["started","completed","failed"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200},"description":"Max results (default 50)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"app_slug":{"type":"string","nullable":true},"action":{"type":"string"},"status":{"type":"string","enum":["started","completed","failed"]},"reserved":{"type":"integer"},"actual_cost":{"type":"integer"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"metadata":{"type":"object","additionalProperties":true,"nullable":true}},"required":["id","action","status","reserved","actual_cost","created_at","updated_at"]}}},"required":["jobs"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Jobs"],"summary":"Create job (reserve credits)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStartRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStartResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs/complete":{"post":{"tags":["Jobs"],"summary":"Complete a job and settle credits","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCompleteRequest"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/jobs/fail":{"post":{"tags":["Jobs"],"summary":"Fail a job and optionally refund the reserve","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobFailRequest"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/xToken":{"get":{"tags":["X Integration"],"summary":"Fetch X/Twitter OAuth access token via Clerk","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/oauthToken/{provider}":{"get":{"tags":["OAuth Tokens"],"summary":"Fetch OAuth access token for a provider via Clerk","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/xMe":{"get":{"tags":["X Integration"],"summary":"Proxy to https://api.twitter.com/2/users/me","security":[{"twitterBearer":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"502":{"description":"Upstream error"}}}},"/api/sessionToken":{"get":{"tags":["Auth"],"summary":"Return a Clerk session JWT (optionally via template)","parameters":[{"name":"template","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"401":{"description":"Unauthorized - includes signin_url for redirect-based auth","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionUnauthorized"}}}},"404":{"description":"Token not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/signin":{"get":{"tags":["Auth"],"summary":"Interactive sign-in. On success, redirects to redirect_to with ?token=<JWT>","parameters":[{"name":"redirect_to","in":"query","required":false,"schema":{"type":"string","format":"uri"},"description":"Absolute URL to send the user back to after sign-in"}],"responses":{"200":{"description":"HTML sign-in UI","content":{"text/html":{"schema":{"type":"string"}}}}}}}}}