{"openapi":"3.1.0","info":{"title":"Data Reaktor Factory API","description":"An agentic AI data factory for B2B outreach — research lookups, personalized copy generation, lead scoring, outreach sequences, and thought leadership content. Built for both humans (fractional consultants, B2B sellers) and AI agents via API key.","version":"1.5.0","contact":{"name":"Data Reaktor Support","url":"https://www.datareaktor.ai"}},"servers":[{"url":"https://www.datareaktor.ai/api/v1","description":"Production server"}],"components":{"securitySchemes":{"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Agent API key — obtain from the My Agents page"},"bearerAuth":{"type":"http","scheme":"bearer","description":"Bearer token from Replit Auth session"}},"schemas":{"OutreachSequenceInput":{"type":"object","required":["contactName","company","contactRole","signals","userValueProp"],"description":"Input for outreach_sequence. contactRole OR role is required (either field accepted at runtime). signals required as array (may be empty). userValueProp required; server enriches with account settings if blank string provided.","properties":{"contactName":{"type":"string","description":"Full name of the prospect","example":"Sarah Chen"},"contactRole":{"type":"string","description":"Job title / role. Runtime alias: role — either field accepted","example":"VP of Sales"},"role":{"type":"string","description":"Alias for contactRole — accepted at runtime in place of contactRole","example":"VP of Sales"},"company":{"type":"string","description":"Company name or domain","example":"Acme Corp"},"industry":{"type":"string","description":"Industry sector (optional)","example":"B2B SaaS"},"signals":{"type":"array","items":{"type":"string"},"description":"Recent signals about the contact/company — funding, hiring, news, job postings. Empty array accepted; server enriches from user context if blank.","example":["Recently raised Series B","Hiring 5 SDRs","Published article on scaling outbound"]},"userValueProp":{"type":"string","description":"Your value proposition. Required — provide empty string to fall back to account setting.","example":"I help Series B SaaS companies build outbound from scratch"},"userTone":{"type":"string","enum":["professional","casual","direct","friendly"],"default":"professional"},"sequenceObjective":{"type":"string","description":"Goal of the sequence","example":"book a discovery call"}}},"OutreachSequenceOutput":{"type":"object","properties":{"contact":{"type":"string","example":"Sarah Chen"},"company":{"type":"string","example":"Acme Corp"},"sequenceStrategy":{"type":"string","description":"2-3 sentence strategic overview of the sequence approach"},"step1_connect":{"type":"string","description":"LinkedIn connect request content (flat key, ≤300 chars)"},"step2_inmail_subject":{"type":"string","description":"LinkedIn InMail subject line (flat key)"},"step2_inmail_1":{"type":"string","description":"LinkedIn InMail body (flat key, ≤400 chars)"},"step2_email_1":{"type":"string","description":"Email follow-up body (flat key, 100-150 words)"},"step3_breakup":{"type":"string","description":"Breakup message (flat key, ≤200 chars)"},"sequence":{"type":"object","properties":{"step1":{"type":"object","description":"LinkedIn Connect Request — Day 1, max 300 chars, no pitch","properties":{"channel":{"type":"string","example":"LinkedIn Connect Request"},"sendTiming":{"type":"string","example":"Day 1"},"content":{"type":"string"},"charCount":{"type":"integer"},"notes":{"type":"string"}}},"step2":{"type":"object","description":"LinkedIn InMail — Day 4 after connection, one signal hook, max 400 chars","properties":{"channel":{"type":"string","example":"LinkedIn InMail"},"sendTiming":{"type":"string","example":"Day 4 — after connection accepted"},"subject":{"type":"string"},"content":{"type":"string"},"charCount":{"type":"integer"},"notes":{"type":"string"}}},"step3":{"type":"object","description":"Email follow-up — Day 8, different angle than step 2, 100-150 words","properties":{"channel":{"type":"string","example":"Email"},"sendTiming":{"type":"string","example":"Day 8 — if no LinkedIn response"},"subject":{"type":"string"},"previewText":{"type":"string"},"content":{"type":"string"},"charCount":{"type":"integer"},"notes":{"type":"string"}}},"step4":{"type":"object","description":"Breakup message — Day 14, graceful close, max 200 chars","properties":{"channel":{"type":"string","example":"LinkedIn Message or Email"},"sendTiming":{"type":"string","example":"Day 14 — final touch"},"content":{"type":"string"},"charCount":{"type":"integer"},"notes":{"type":"string"}}}}}}},"Task":{"type":"object","properties":{"taskId":{"type":"string","example":"AT-abc123xyz456"},"taskType":{"type":"string","enum":["personalized_email","linkedin_message","lead_scoring","cold_call_script","outreach_sequence","thought_leadership","outreach_generation","research"]},"status":{"type":"string","enum":["queued","processing","completed","failed"]},"inputPayload":{"type":"object"},"outputPayload":{"type":"object","description":"Present for native tasks on first response (synchronous). Async tasks use GET /tasks/{taskId} to poll."},"creditsCharged":{"type":"integer"},"tokensUsed":{"type":"integer","nullable":true},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true}}},"Skill":{"type":"object","properties":{"taskType":{"type":"string"},"filename":{"type":"string"},"purpose":{"type":"string"},"model":{"type":"string"},"isNative":{"type":"boolean","description":"true = executed synchronously by Replit; false = routed to n8n"}}},"WorkflowDefinition":{"type":"object","properties":{"id":{"type":"string","example":"cold-outreach-full"},"name":{"type":"string","example":"Cold Outreach Full Pipeline"},"description":{"type":"string"},"creditCost":{"type":"integer","example":22},"stepCount":{"type":"integer","example":4},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"taskType":{"type":"string"},"description":{"type":"string"}}}},"inputSchema":{"type":"object"},"outputSchema":{"type":"object"}}},"AgentSummary":{"type":"object","description":"Public-facing fields for a sub-agent. Returned by GET /agents and GET /agents/:id. Never includes the raw API key — only the prefix.","properties":{"id":{"type":"integer","example":42},"name":{"type":"string","example":"Outreach worker — Acme pipeline"},"apiKeyPrefix":{"type":"string","example":"drk_xxxxxxxxxxxx"},"tier":{"type":"string","enum":["free","starter","growth","scale","pro"]},"callbackUrl":{"type":"string","format":"uri","nullable":true},"status":{"type":"string","enum":["active","suspended","revoked"]},"monthlyCredits":{"type":"integer","description":"Per-agent monthly credit cap (0 = no cap)"},"usedCredits":{"type":"integer","description":"Credits spent this billing period"},"canProvision":{"type":"boolean","description":"True if this key can mint sub-agents and call /billing/* endpoints on behalf of its owner"},"apiKeyExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"Only present on GET /agents/:id"},"createdAt":{"type":"string","format":"date-time"}}},"AgentWithKey":{"allOf":[{"$ref":"#/components/schemas/AgentSummary"},{"type":"object","required":["agentId","apiKey"],"properties":{"agentId":{"type":"integer","description":"Alias of `id` for back-compat with older clients","example":42},"apiKey":{"type":"string","description":"The full API key, returned EXACTLY ONCE — store it immediately. Format: `drk_<32 url-safe bytes>`.","example":"drk_THIS_IS_SHOWN_EXACTLY_ONCE_STORE_IT_NOW"}}}]},"RotatedAgentKey":{"type":"object","required":["agentId","apiKey","apiKeyPrefix","apiKeyExpiresAt"],"description":"Response from POST /agents/:id/rotate-key. The full apiKey is returned exactly once — old key is invalidated immediately.","properties":{"agentId":{"type":"integer","example":42},"id":{"type":"integer","description":"Alias of agentId","example":42},"name":{"type":"string","example":"Outreach worker — Acme pipeline"},"apiKeyPrefix":{"type":"string","example":"drk_yyyyyyyyyyyy"},"apiKey":{"type":"string","description":"Full new API key — store immediately, only shown once.","example":"drk_NEW_KEY_SHOWN_ONCE"},"apiKeyExpiresAt":{"type":"string","format":"date-time","example":"2027-04-26T00:00:00.000Z"}}},"BillingBalanceUser":{"type":"object","required":["principal","userId","purchasedCredits"],"description":"Returned by GET /billing/credits when called by a human / owner key without ?agentId=","properties":{"principal":{"type":"string","enum":["user"]},"userId":{"type":"string","example":"replit_user_abc123"},"purchasedCredits":{"type":"integer","example":4500},"monthlyRecordsRemaining":{"type":"integer","example":8200},"monthlyRecordsLimit":{"type":"integer","example":10000}}},"BillingBalanceAgent":{"type":"object","required":["principal","agentId","balance"],"description":"Returned by GET /billing/credits when an agent key calls without ?agentId=, or when ?agentId= targets an owned sub-agent.","properties":{"principal":{"type":"string","enum":["agent"]},"agentId":{"type":"integer","example":42},"balance":{"type":"integer","description":"Currently available credits on this sub-agent","example":500},"monthlyCreditCap":{"type":"integer","example":1000},"usedCredits":{"type":"integer","example":250}}},"CreditTransferResult":{"type":"object","required":["success","agentId","creditsTransferred","userPurchasedCredits","agentBalance"],"properties":{"success":{"type":"boolean","enum":[true]},"agentId":{"type":"integer","example":42},"creditsTransferred":{"type":"integer","example":500},"userPurchasedCredits":{"type":"integer","description":"Owner's remaining purchased_credits balance after the move","example":4500},"agentBalance":{"type":"integer","description":"Sub-agent's new balance after the move","example":500}}},"CheckoutSession":{"type":"object","required":["checkoutUrl","sessionId"],"description":"Stripe Checkout session. Open `checkoutUrl` in a browser to complete payment.","properties":{"checkoutUrl":{"type":"string","format":"uri","example":"https://checkout.stripe.com/c/pay/cs_test_..."},"sessionId":{"type":"string","example":"cs_test_a1B2c3D4..."},"url":{"type":"string","format":"uri","description":"Back-compat alias of `checkoutUrl` for older SDKs"}}},"ProvisioningError":{"type":"object","required":["error"],"description":"Standard error envelope used by /agents and /billing/* endpoints. `code` is a stable string suitable for switch/case logic — see GET /errors for the full registry.","properties":{"error":{"type":"string","description":"Human-readable summary"},"message":{"type":"string","description":"Optional longer human-readable explanation. Currently set on the BETA_CHECKOUT_DISABLED 503 response."},"code":{"type":"string","description":"Machine-readable error code","enum":["OWNER_SCOPE_REQUIRED","BETA_CHECKOUT_DISABLED","INSUFFICIENT_CREDITS","AGENT_NOT_OWNED","INVALID_AMOUNT","CANNOT_REVOKE_SELF"]},"details":{"description":"Optional Zod validation details for 400 responses"},"available":{"type":"integer","description":"Available credits — present on 402 INSUFFICIENT_CREDITS"},"requested":{"type":"integer","description":"Requested credits — present on 402 INSUFFICIENT_CREDITS"}}}}},"security":[{"apiKeyHeader":[]},{"bearerAuth":[]}],"paths":{"/tasks":{"post":{"summary":"Submit a task","description":"Submit a task for execution. Native tasks (personalized_email, linkedin_message, lead_scoring, cold_call_script, outreach_sequence, thought_leadership) return output synchronously. n8n-backed tasks (outreach_generation, research) are async — poll GET /tasks/{taskId}.","operationId":"createTask","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["taskType","inputPayload"],"properties":{"taskType":{"type":"string","enum":["personalized_email","linkedin_message","lead_scoring","cold_call_script","outreach_sequence","thought_leadership","outreach_generation","research"]},"inputPayload":{"type":"object","description":"Schema varies by taskType — see GET /skills for per-skill input schemas"},"callbackUrl":{"type":"string","format":"uri","description":"Optional webhook URL for async task completion notification"}}},"example":{"taskType":"personalized_email","inputPayload":{"contactName":"Sarah Chen","contactRole":"VP of Sales","company":"Acme Corp","industry":"B2B SaaS","signals":["Recently raised Series B","Hiring 3 SDRs"],"userValueProp":"I help Series A-B SaaS companies build outbound from scratch as a fractional VP of Sales"}}}}},"responses":{"200":{"description":"Native task completed synchronously — output included","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Task"},{"type":"object","properties":{"output":{"type":"object"},"tokensUsed":{"type":"integer"}}}]}}}},"202":{"description":"Async task accepted — poll GET /tasks/{taskId} for results","content":{"application/json":{"schema":{"type":"object","properties":{"taskId":{"type":"string"},"status":{"type":"string","enum":["queued"]},"estimatedCredits":{"type":"integer"}}}}}},"400":{"description":"Invalid task type or input payload"},"401":{"description":"Missing or invalid API key"},"402":{"description":"Insufficient credits"}}},"get":{"summary":"List tasks","operationId":"listTasks","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of tasks","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"count":{"type":"integer"}}}}}}}}}}},"/tasks/{taskId}":{"get":{"summary":"Get task details","operationId":"getTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Task details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"404":{"description":"Task not found"}}},"delete":{"summary":"Cancel a queued task","operationId":"cancelTask","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Task cancelled and credits refunded"},"400":{"description":"Task cannot be cancelled (already processing or completed)"}}}},"/skills":{"get":{"summary":"List available skills","description":"Returns all available skill definitions including their purpose, model, and whether they execute natively or via n8n","operationId":"listSkills","security":[],"responses":{"200":{"description":"Skills registry","content":{"application/json":{"schema":{"type":"object","properties":{"skills":{"type":"array","items":{"$ref":"#/components/schemas/Skill"}}}}}}}}}},"/capabilities":{"get":{"summary":"List service capabilities and credit costs","operationId":"getCapabilities","security":[],"responses":{"200":{"description":"Capabilities including individual task types and multi-step workflows","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"services":{"type":"array","items":{"type":"object","properties":{"taskType":{"type":"string"},"credits":{"type":"integer"},"rateLimit":{"type":"integer"}}}},"workflows":{"type":"array","description":"Pre-built multi-step workflow definitions","items":{"$ref":"#/components/schemas/WorkflowDefinition"}}}}}}}}}},"/workflows":{"get":{"summary":"List available multi-step workflows","description":"Returns all pre-built workflow definitions with their steps, credit costs, and input/output schemas. No authentication required.","operationId":"listWorkflows","security":[],"responses":{"200":{"description":"Workflow registry","content":{"application/json":{"schema":{"type":"object","properties":{"workflows":{"type":"array","items":{"$ref":"#/components/schemas/WorkflowDefinition"}}}}}}}}},"post":{"summary":"Submit a multi-step workflow","description":"Submit a named workflow with a single input payload. Credits for all steps are charged upfront. Returns immediately with a taskId — poll GET /tasks/{taskId} for step-by-step progress.","operationId":"submitWorkflow","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workflowId","inputPayload"],"properties":{"workflowId":{"type":"string","enum":["cold-outreach-full","lead-qualification","account-research"],"description":"The workflow to execute — use GET /workflows to list available options"},"inputPayload":{"type":"object","description":"Workflow input — schema varies by workflowId, see GET /workflows for inputSchema"},"callbackUrl":{"type":"string","format":"uri","description":"Optional webhook URL — receives the final workflow result when all steps complete"}}},"example":{"workflowId":"cold-outreach-full","inputPayload":{"domain":"acme.com","userValueProp":"I help Series B SaaS companies build outbound from scratch","userTone":"professional"}}}}},"responses":{"202":{"description":"Workflow accepted — poll GET /tasks/{taskId} for progress","content":{"application/json":{"schema":{"type":"object","properties":{"taskId":{"type":"string","example":"WF-abc123xyz456"},"workflowId":{"type":"string"},"workflowName":{"type":"string"},"status":{"type":"string","enum":["processing"]},"totalSteps":{"type":"integer"},"creditsCharged":{"type":"integer"},"message":{"type":"string"}}}}}},"400":{"description":"Unknown workflowId or invalid input"},"401":{"description":"Missing or invalid API key"},"402":{"description":"Insufficient credits"}}}},"/agents":{"get":{"summary":"List sub-agents","description":"Owner-scoped list of sub-agents.\n\n- A human session or an owner key (`canProvision=true`) returns every agent owned by the calling user.\n- A non-owner agent key returns a single-element array containing only itself, so the agent can confirm its own identity without leaking siblings.","operationId":"listAgents","tags":["Provisioning"],"responses":{"200":{"description":"Array of agent summaries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentSummary"}}}}},"401":{"description":"Authentication required"}}},"post":{"summary":"Create a sub-agent","description":"Mint a new sub-agent and its API key in one call. The full API key is returned **exactly once** in the response body — store it immediately.\n\n**Requires owner scope**: a Replit Auth session, or an API key with `canProvision=true`.\n\nRate-limited to 20 creates per hour per owning user (in addition to the global 60 req/min).","operationId":"createAgent","tags":["Provisioning"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"example":"Outreach worker — Acme pipeline"},"tier":{"type":"string","enum":["free","starter","growth","scale","pro"],"default":"free"},"callbackUrl":{"type":"string","format":"uri","description":"Optional webhook for async task completion. Empty string accepted as null.","example":"https://your.app/webhooks/datareaktor"},"monthlyCreditCap":{"type":"integer","minimum":0,"default":0,"description":"Per-agent monthly cap; 0 means no cap (subject to user-level limits)"}}}}}},"responses":{"201":{"description":"Agent created. The `apiKey` field is returned ONCE — store it immediately.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWithKey"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Validation error","details":[{"path":["name"],"message":"String must contain at least 1 character(s)"}]}}}},"401":{"description":"Authentication required"},"403":{"description":"Caller authenticated but lacks owner scope (`canProvision=true` or Replit Auth session).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Owner scope required to provision sub-agents.","code":"OWNER_SCOPE_REQUIRED"}}}},"429":{"description":"Rate limit exceeded — max 20 sub-agent creates per hour per owner"}}}},"/agents/{id}":{"get":{"summary":"Get sub-agent detail","description":"Fetch a single sub-agent's metadata.\n\n- Owners can fetch any agent they own.\n- A non-owner agent key may only fetch its own row (any other id returns 403 `OWNER_SCOPE_REQUIRED`).","operationId":"getAgent","tags":["Provisioning"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Numeric agent id"}],"responses":{"200":{"description":"Agent detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSummary"}}}},"400":{"description":"Invalid agent id (non-numeric)"},"403":{"description":"Non-owner agent key tried to inspect a sibling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"This agent key can only inspect itself. Use an owner key or Replit Auth session to inspect siblings.","code":"OWNER_SCOPE_REQUIRED"}}}},"404":{"description":"Agent not found, or not owned by caller"}}},"delete":{"summary":"Revoke a sub-agent","description":"Revoke a sub-agent permanently. **Requires owner scope.** An agent key cannot revoke itself — use a different owner key or a Replit Auth session.","operationId":"revokeAgent","tags":["Provisioning"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Agent revoked","content":{"application/json":{"schema":{"type":"object","required":["success","agentId"],"properties":{"success":{"type":"boolean","enum":[true]},"agentId":{"type":"integer"}}}}}},"403":{"description":"Owner scope required (Replit Auth session OR API key with `canProvision=true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Owner scope required to call this endpoint.","code":"OWNER_SCOPE_REQUIRED"}}}},"404":{"description":"Agent not found, or not owned by caller"},"409":{"description":"An agent key cannot revoke itself.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"An agent key cannot revoke itself. Use a different owner key or Replit Auth session.","code":"CANNOT_REVOKE_SELF"}}}}}}},"/agents/{id}/rotate-key":{"post":{"summary":"Rotate a sub-agent's API key","description":"Generate a fresh API key for the target sub-agent. The previous key is invalidated immediately. The new key is returned **exactly once**.\n\n**Requires owner scope.**","operationId":"rotateAgentKey","tags":["Provisioning"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"New API key issued. Store it immediately — it will not be shown again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotatedAgentKey"}}}},"400":{"description":"Invalid agent id"},"403":{"description":"Owner scope required (Replit Auth session OR API key with `canProvision=true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Owner scope required to call this endpoint.","code":"OWNER_SCOPE_REQUIRED"}}}},"404":{"description":"Agent not found, or not owned by caller"}}}},"/billing/checkout":{"post":{"summary":"Start a Stripe checkout for credits or a subscription","description":"Programmatic counterpart to the web checkout flow. **Requires owner scope.**\n\nReturns a `checkoutUrl` for the calling agent to surface to its operator. While the platform is in beta, this endpoint returns `503` with `code=BETA_CHECKOUT_DISABLED` so callers can detect the gate cleanly and fall back to manual credit grants.","operationId":"createBillingCheckout","tags":["Provisioning","Billing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Provide `pack` (preferred) or `packId` (legacy alias).","properties":{"pack":{"type":"string","description":"Pay-as-you-go pack id (`pack_basic`, `pack_plus`, `pack_max`) or subscription plan id (`starter`, `growth`, `scale`).","example":"pack_basic"},"packId":{"type":"string","description":"Alias for `pack`"},"agentId":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Optional sub-agent id to credit on success. Omit to credit the owning user's purchased_credits balance.","example":42}}},"example":{"pack":"pack_basic","agentId":42}}}},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"}}}},"400":{"description":"Missing `pack` / `packId`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"}}}},"403":{"description":"Owner scope required (Replit Auth session OR API key with `canProvision=true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Owner scope required to call this endpoint.","code":"OWNER_SCOPE_REQUIRED"}}}},"503":{"description":"Beta gate: paid checkout is disabled. Email support@datareaktor.app for credit grants.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"beta_mode_active","message":"Paid checkout is disabled in beta. Contact support@datareaktor.app for credit grants.","code":"BETA_CHECKOUT_DISABLED"}}}}}}},"/billing/transfer":{"post":{"summary":"Transfer credits from owner balance to a sub-agent","description":"Atomically debits the owning user's `purchased_credits` and credits the target sub-agent's balance. **Requires owner scope.**\n\nWrites a `[Audit][provision]` log line and a `billing_events` row with `eventType=credit_purchased` for the audit trail.","operationId":"transferCreditsToAgent","tags":["Provisioning","Billing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agentId","credits"],"properties":{"agentId":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Numeric id of the sub-agent to credit (string accepted and coerced)."},"credits":{"type":"integer","minimum":1,"description":"Positive integer credits to move."}}},"example":{"agentId":42,"credits":500}}}},"responses":{"200":{"description":"Credits moved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditTransferResult"}}}},"400":{"description":"Validation error or invalid amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Invalid credit amount — must be a positive integer","code":"INVALID_AMOUNT"}}}},"402":{"description":"Owner does not have enough purchased credits to cover the move","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Insufficient purchased credits to transfer","code":"INSUFFICIENT_CREDITS","available":120,"requested":500}}}},"403":{"description":"Owner scope required (Replit Auth session OR API key with `canProvision=true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Owner scope required to call this endpoint.","code":"OWNER_SCOPE_REQUIRED"}}}},"404":{"description":"Target agent does not exist or is not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"Agent not found or not owned by caller","code":"AGENT_NOT_OWNED"}}}}}}},"/billing/credits":{"get":{"summary":"Get the calling principal's credit balance","description":"Behaviour depends on caller and `?agentId=` query:\n\n- Agent key, no `?agentId=` → that agent's own balance + cap (`BillingBalanceAgent`).\n- Owner key or human session, no `?agentId=` → user-level `purchasedCredits` + monthly records remaining (`BillingBalanceUser`).\n- Any caller with `?agentId=` → balance for the specified sub-agent if owned (or self, for non-owner agent keys). Returns 403 `OWNER_SCOPE_REQUIRED` for siblings, 404 for unowned ids.","operationId":"getBillingCredits","tags":["Provisioning","Billing"],"parameters":[{"name":"agentId","in":"query","required":false,"schema":{"type":"integer"},"description":"Optional sub-agent id to query. Omit to get the calling principal's own balance."}],"responses":{"200":{"description":"Credit balance — shape depends on caller and `agentId`","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/BillingBalanceUser"},{"$ref":"#/components/schemas/BillingBalanceAgent"}]}}}},"401":{"description":"Authentication required"},"403":{"description":"Non-owner agent key tried to inspect a sibling's balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningError"},"example":{"error":"This agent key can only inspect its own balance.","code":"OWNER_SCOPE_REQUIRED"}}}},"404":{"description":"Agent not found or not owned by caller"}}}}},"x-workflow-progress":{"description":"When polling GET /tasks/{taskId} for a workflow task, outputPayload contains step-level progress","schema":{"type":"object","properties":{"workflowId":{"type":"string"},"workflowName":{"type":"string"},"totalSteps":{"type":"integer"},"completedSteps":{"type":"integer"},"currentStep":{"type":"integer"},"status":{"type":"string","enum":["running","completed","partial","failed"]},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"taskType":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","skipped"]},"output":{"type":"object"},"error":{"type":"string"}}}},"finalOutput":{"type":"object"}}}}}