Skip to main content
POST
/
api
/
stealthify
/
agent
/
runs
Stealth Agent Async Runs
curl --request POST \
  --url https://stealthgpt.ai/api/stealthify/agent/runs \
  --header 'Content-Type: <content-type>' \
  --header 'api-token: <api-token>' \
  --data '
{
  "preset": "<string>",
  "prompt": "<string>",
  "platform": "<string>",
  "enableFactCheck": true,
  "enableImageGeneration": true,
  "webhookUrl": "<string>",
  "webhookSecret": "<string>"
}
'
{
  "runId": "<string>",
  "status": "<string>",
  "statusUrl": "<string>",
  "preset": "<string>",
  "createdAt": "<string>",
  "updatedAt": "<string>",
  "currentStepId": {},
  "currentOperation": {},
  "documentId": "<string>",
  "result": "<string>",
  "outputWords": 123,
  "creditsSpent": 123,
  "remainingCredits": 123,
  "billingMode": "<string>",
  "meteredChargedCredits": 123,
  "content-type": "<string>",
  "user-agent": "<string>",
  "x-stealthgpt-event": "<string>",
  "x-stealthgpt-run-id": "<string>",
  "x-stealthgpt-timestamp": "<string>",
  "x-stealthgpt-signature": "<string>"
}

Documentation Index

Fetch the complete documentation index at: https://docs.stealthgpt.ai/llms.txt

Use this file to discover all available pages before exploring further.

The async Stealth Agent API lets you start a long-running generation job, return immediately, and receive the final result later by polling or webhook callback. Use it for integrations, automations, UI backends, and agent platforms where a multi-minute HTTP request is unreliable.
If you need a simple one-off script and can keep the connection open for up to 10 minutes, use the synchronous POST /api/stealthify/agent endpoint. For production integrations, prefer async runs.

Flow

1

Create a run

Send POST /api/stealthify/agent/runs with the same preset body as the synchronous endpoint. Optionally include webhookUrl and idempotency-key.
2

Store the run id

The API returns 202 Accepted with runId, status: "queued", and statusUrl.
3

Poll or wait for webhook

Call GET /api/stealthify/agent/runs/{runId} until status is completed, failed, or cancelled. If webhookUrl was provided, StealthGPT also sends the terminal payload to that URL.

Authentication

api-token
string
required
Your Stealth API token from the Stealth API dashboard.

Create a run

POST https://stealthgpt.ai/api/stealthify/agent/runs

Headers

api-token
string
required
Your Stealth API token.
Content-Type
string
required
Must be application/json.
idempotency-key
string
Optional key, 1-255 characters. Reuse the same key when retrying the same create request to avoid enqueueing duplicate runs.

Request body

The async create body uses the same strict preset discriminated union as POST /api/stealthify/agent, plus optional webhookUrl and webhookSecret.
preset
string
required
One of "academic", "seo", or "social".
prompt
string
required
Topic or instructions for the document (non-empty).
platform
string
Required only when preset is "social". Must be "linkedin" or "medium".
enableFactCheck
boolean
default:"false"
When true, runs a fact-check pass on the draft and applies fixes before humanization.
enableImageGeneration
boolean
default:"false"
When true, may embed generated images in the final markdown where supported.
webhookUrl
string
Optional URL that receives a POST callback when the run reaches a terminal status.
webhookSecret
string
Optional signing secret, 16-255 characters. When present, StealthGPT signs webhook callbacks with HMAC-SHA256.

Response: 202 Accepted

runId
string
required
Identifier of the queued Stealth Agent run.
status
string
required
Always "queued" for a successful create response.
statusUrl
string
required
Relative polling URL: /api/stealthify/agent/runs/{runId}.
{
  "runId": "run_abc123",
  "status": "queued",
  "statusUrl": "/api/stealthify/agent/runs/run_abc123"
}

Poll run status

GET https://stealthgpt.ai/api/stealthify/agent/runs/{runId}

Headers

api-token
string
required
Your Stealth API token. The run must belong to this token’s account.

Path parameters

runId
string
required
The runId returned from POST /api/stealthify/agent/runs.

Queued response

Returned before the generation row has started.
{
  "runId": "run_abc123",
  "status": "queued"
}

Running response

runId
string
required
Run identifier.
preset
string
required
academic, seo, or social.
status
string
required
"running".
createdAt
string
required
ISO timestamp for run creation.
updatedAt
string
required
ISO timestamp for the latest run update.
currentStepId
string | null
required
Current pipeline step, or null while the run is starting. Possible step ids include web_research, keywords_research, competitor_analysis, social_research, generate_text, meta_generation, fact_check, humanize, image_generation, and citations.
currentOperation
string | null
required
Human-readable current operation, or null while the run is starting.
{
  "runId": "run_abc123",
  "preset": "academic",
  "createdAt": "2026-05-27T14:55:00.000Z",
  "updatedAt": "2026-05-27T14:56:22.000Z",
  "currentStepId": "humanize",
  "currentOperation": "Humanizing draft",
  "status": "running"
}

Completed response

The completed polling response is also the successful webhook callback body.
documentId
string
required
Identifier of the saved document record for this run.
result
string
required
Final content as markdown.
outputWords
number
required
Word count of result used for billing.
creditsSpent
number
required
Stealth API words charged for this request.
remainingCredits
number
required
Prepaid word balance after this charge.
billingMode
string
required
"prepaid" when covered from balance, or "payg" when metered usage was applied.
meteredChargedCredits
number
required
Words billed to metered usage for this request (0 when fully prepaid).
{
  "runId": "run_abc123",
  "preset": "academic",
  "createdAt": "2026-05-27T14:55:00.000Z",
  "updatedAt": "2026-05-27T14:59:12.000Z",
  "currentStepId": "citations",
  "currentOperation": "Inserting citations",
  "status": "completed",
  "documentId": "doc_123",
  "result": "# Creatine and resistance training in older adults\n\n...",
  "outputWords": 1420,
  "creditsSpent": 14200,
  "remainingCredits": 485800,
  "billingMode": "prepaid",
  "meteredChargedCredits": 0
}

Failed response

failed can mean generation failed or billing could not be completed after generation.
{
  "runId": "run_abc123",
  "preset": "academic",
  "createdAt": "2026-05-27T14:55:00.000Z",
  "updatedAt": "2026-05-27T14:58:30.000Z",
  "currentStepId": "generate_text",
  "currentOperation": "Generating draft",
  "status": "failed",
  "error": {
    "code": "generation_failed",
    "message": "Stealth Agent generation failed"
  }
}
Possible error.code values:
CodeMeaning
generation_failedThe generation pipeline failed before a completed document was available.
payg_terms_requiredCredits ran out and pay-as-you-go terms have not been accepted.
payment_requiredCredits or pay-as-you-go access were not available when billing was finalized.
billing_delayedBilling was temporarily delayed; create a new run later if needed.
cancelledThe run was cancelled before completion.

Cancelled response

{
  "runId": "run_abc123",
  "preset": "academic",
  "createdAt": "2026-05-27T14:55:00.000Z",
  "updatedAt": "2026-05-27T14:56:10.000Z",
  "currentStepId": "web_research",
  "currentOperation": "Researching sources",
  "status": "cancelled",
  "error": {
    "code": "cancelled",
    "message": "Stealth Agent generation was cancelled"
  }
}

Webhook callbacks

If webhookUrl is present in the create request, StealthGPT sends a best-effort POST when the run reaches a terminal status: completed, failed, or cancelled.

Headers

content-type
string
application/json
user-agent
string
StealthGPT-API-Webhook/1.0
x-stealthgpt-event
string
stealth_agent.run.completed, stealth_agent.run.failed, or stealth_agent.run.cancelled.
x-stealthgpt-run-id
string
The run id.
x-stealthgpt-timestamp
string
Unix timestamp in seconds. Present on every webhook callback.
x-stealthgpt-signature
string
Present when webhookSecret was provided. Format: v1={hex_hmac_sha256}.

Delivery behavior

  • Webhook delivery uses a 10-second timeout.
  • Network errors, timeouts, 429, and 5xx responses are retried up to 3 attempts.
  • 4xx responses other than 429 are treated as final failures.
  • Webhook delivery is best-effort: a failed callback does not change the run status.
  • The callback body is the same terminal payload returned by GET /api/stealthify/agent/runs/{runId}.
  • When webhookSecret is provided, the signature is computed over ${timestamp}.${rawBody} using HMAC-SHA256.
  • Reject signed callbacks with old timestamps. A 5-minute tolerance is recommended.

Verify a signature

Use the raw request body exactly as received. Re-serializing parsed JSON can change whitespace and produce a different signature.
nodejs
import { createHmac, timingSafeEqual } from 'node:crypto'

const WEBHOOK_TOLERANCE_SECONDS = 5 * 60

function verifyStealthGptWebhook({ rawBody, timestamp, signature, secret }) {
  if (!signature) return false

  const timestampSeconds = Number(timestamp)
  const nowSeconds = Math.floor(Date.now() / 1000)

  if (
    !Number.isFinite(timestampSeconds) ||
    Math.abs(nowSeconds - timestampSeconds) > WEBHOOK_TOLERANCE_SECONDS
  ) {
    return false
  }

  const [version, receivedDigest] = signature.split('=')

  if (version !== 'v1' || !receivedDigest) return false

  const expectedDigest = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex')

  const expected = Buffer.from(expectedDigest, 'hex')
  const received = Buffer.from(receivedDigest, 'hex')

  return (
    expected.length === received.length &&
    timingSafeEqual(expected, received)
  )
}

Examples

curl --request POST \
  --url 'https://stealthgpt.ai/api/stealthify/agent/runs' \
  --header 'Content-Type: application/json' \
  --header 'api-token: YOUR_API_TOKEN' \
  --header 'idempotency-key: article-123' \
  --data '{
    "preset": "academic",
    "prompt": "Outline creatine benefits for older adults in resistance training",
    "enableFactCheck": true,
    "enableImageGeneration": false,
    "webhookUrl": "https://example.com/webhooks/stealth-agent",
    "webhookSecret": "whsec_your_random_signing_secret"
  }'

Errors

EndpointStatusMeaning
POST /runs400Invalid request body or idempotency-key.
POST /runs401Missing api-token header or token not found.
POST /runs402Insufficient credits, pay-as-you-go not available, payment method missing, or billing cap delay.
POST /runs500Server error while creating the run.
GET /runs/{runId}400Invalid runId.
GET /runs/{runId}401Missing api-token header or token not found.
GET /runs/{runId}404Run not found or belongs to another account.
GET /runs/{runId}500Server error while reading status.

Usage notes

  • Use idempotency-key whenever your create request can be retried by a queue, workflow engine, or HTTP client.
  • Poll every 5-15 seconds; there is no benefit to polling every second.
  • Store runId, statusUrl, and your own job id together so webhook handlers can reconcile callbacks with queued work.
  • Billing uses output words only: charged words = ceil(outputWords × 10) toward your Stealth API balance.