VHGENGINE
REMOTE · ENGINE-PRODUCTION-3BDC.UP.RAILWAY.APP · SCANNED AUG 3
Agents-first viral-hook engine: generate, score, and remix short-form hooks over MCP.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score →
Endpoint Security63
- The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
- Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (delete_account). See how to fix → View diagnostics → Fail
- HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
- The HSTS (Strict-Transport-Security) header is present. View diagnostics → Pass
- DNSSEC check failed: this domain isn't protected by DNSSEC. See how to fix → View diagnostics → Fail
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability53
- 43% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Partial
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 14279 tokens (~274/item across 52 items; 38 tools + 14 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management27
- Stability observed for 8 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 100% of tool parameters carry a description.Pass
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Add this component to your MCP client. Where a client-specific snippet is available, pick your client below and copy it straight into your config; otherwise use the connection detail shown.
remote · engine-production-3bdc.up.railway.app
claude mcp add --transport http app-railway-up-engine-production-3bdc-vhgengine https://engine-production-3bdc.up.railway.app/mcp
[mcp_servers.app-railway-up-engine-production-3bdc-vhgengine] url = "https://engine-production-3bdc.up.railway.app/mcp"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"app-railway-up-engine-production-3bdc-vhgengine": {
"type": "remote",
"url": "https://engine-production-3bdc.up.railway.app/mcp",
"enabled": true
}
}
} openclaw mcp add app-railway-up-engine-production-3bdc-vhgengine --url https://engine-production-3bdc.up.railway.app/mcp --transport streamable-http
mcp_servers:
app-railway-up-engine-production-3bdc-vhgengine:
url: "https://engine-production-3bdc.up.railway.app/mcp" {
"mcpServers": {
"app-railway-up-engine-production-3bdc-vhgengine": {
"type": "http",
"url": "https://engine-production-3bdc.up.railway.app/mcp"
}
}
} The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 3 Aug 26 +6
- The server rewrote its instructions, which are the text every model session reads security
- New tool “cancel_job”, which the server declares destructive security
- Tool “delete_account” rewrote its description, which is the text the model reads security
- Tool “generate_hooks” rewrote its description, which is the text the model reads security
- Tool “generate_hooks_batch” rewrote its description, which is the text the model reads security
- Tool “get_account” rewrote its description, which is the text the model reads security
- Tool “get_usage” rewrote its description, which is the text the model reads security
- Tool “health” rewrote its description, which is the text the model reads security
- Tool “list_hooks” rewrote its description, which is the text the model reads security
- Tool “remix_hook” rewrote its description, which is the text the model reads security
- Tool “score_hook” rewrote its description, which is the text the model reads security
- Tool “signup” rewrote its description, which is the text the model reads security
- Tool “start_generate_job” rewrote its description, which is the text the model reads security
- Schema quality: 138 → 274 ▼ functional
- Schema quality: 0% → 43% ▲ functional
- Tool coverage: 0% → 100% ▲ functional
- Schema quality: good → excellent functional
- New resource “client-guide-claude-desktop” functional
- New resource “client-guide-codex” functional
- New resource “client-guide-cursor” functional
- New resource “client-guide-rest” functional
- New resource “sample” functional
- New resource “client-guide-chatgpt” functional
- New resource “client-guide-claude-code” functional
- New tool “quote” functional
- New tool “export_usage” functional
- New tool “list_billing_events” functional
- New tool “list_runs” functional
- New tool “wait_for_job” functional
- “get_usage” added an optional parameter “offset” cosmetic
- “remix_hook” added an optional parameter “archetype” cosmetic
- “remix_hook” added an optional parameter “hook_id” cosmetic
- “remix_hook” added an optional parameter “platform” cosmetic
- “remix_hook” added an optional parameter “topic” cosmetic
- “score_hook” added an optional parameter “topic” cosmetic
- “score_hooks_batch” added an optional parameter “topic” cosmetic
- “add_credits” reworded the description of “admin_key” cosmetic
- “add_credits” reworded the description of “amount” cosmetic
- “add_credits” reworded the description of “api_key” cosmetic
- “add_credits” reworded the description of “idempotency_key” cosmetic
- “create_checkout” reworded the description of “api_key” cosmetic
- “create_checkout” reworded the description of “cancel_url” cosmetic
- “create_checkout” reworded the description of “pack” cosmetic
- “create_checkout” reworded the description of “success_url” cosmetic
- “create_key” reworded the description of “api_key” cosmetic
- “create_key” reworded the description of “daily_credit_cap” cosmetic
- “create_key” reworded the description of “name” cosmetic
- “create_key” reworded the description of “scopes” cosmetic
- “delete_account” reworded the description of “api_key” cosmetic
- “delete_account” reworded the description of “confirm” cosmetic
- “delete_webhook” reworded the description of “api_key” cosmetic
- “generate_hooks” reworded the description of “api_key” cosmetic
- “generate_hooks” reworded the description of “archetypes” cosmetic
- “generate_hooks” reworded the description of “count” cosmetic
- “generate_hooks” reworded the description of “deadline_ms” cosmetic
- “generate_hooks” reworded the description of “idempotency_key” cosmetic
- “generate_hooks” reworded the description of “mode” cosmetic
- “generate_hooks” reworded the description of “platform” cosmetic
- “generate_hooks” reworded the description of “style” cosmetic
- “generate_hooks” reworded the description of “tags” cosmetic
- “generate_hooks” reworded the description of “topic” cosmetic
- “generate_hooks” reworded the description of “verbosity” cosmetic
- “generate_hooks_batch” reworded the description of “api_key” cosmetic
- “generate_hooks_batch” reworded the description of “idempotency_key” cosmetic
- “generate_hooks_batch” reworded the description of “mode” cosmetic
- “generate_hooks_batch” reworded the description of “platform” cosmetic
- “generate_hooks_batch” reworded the description of “requests” cosmetic
- “generate_hooks_batch” reworded the description of “tags” cosmetic
- “generate_hooks_batch” reworded the description of “verbosity” cosmetic
- “get_account” reworded the description of “api_key” cosmetic
- “get_activity” reworded the description of “api_key” cosmetic
- “get_activity” reworded the description of “recent_limit” cosmetic
- “get_hook” reworded the description of “api_key” cosmetic
- “get_hook” reworded the description of “hook_id” cosmetic
- “get_job” reworded the description of “api_key” cosmetic
- “get_job” reworded the description of “job_id” cosmetic
- “get_usage” reworded the description of “api_key” cosmetic
- “get_usage” reworded the description of “key_prefix” cosmetic
- “get_usage” reworded the description of “recent_limit” cosmetic
- “get_usage” reworded the description of “request_id” cosmetic
- “get_usage” reworded the description of “tag” cosmetic
- “get_webhook” reworded the description of “api_key” cosmetic
- “list_hooks” reworded the description of “api_key” cosmetic
- “list_hooks” reworded the description of “limit” cosmetic
- “list_hooks” reworded the description of “mode” cosmetic
- “list_hooks” reworded the description of “offset” cosmetic
- “list_hooks” reworded the description of “since” cosmetic
- “list_hooks” reworded the description of “tag” cosmetic
- “list_hooks” reworded the description of “topic” cosmetic
- “list_hooks” reworded the description of “unreported” cosmetic
- “list_jobs” reworded the description of “api_key” cosmetic
- “list_jobs” reworded the description of “limit” cosmetic
- “list_jobs” reworded the description of “offset” cosmetic
- “list_keys” reworded the description of “api_key” cosmetic
- “list_outcomes” reworded the description of “api_key” cosmetic
- “list_outcomes” reworded the description of “hook_id” cosmetic
- “list_outcomes” reworded the description of “limit” cosmetic
- “list_outcomes” reworded the description of “offset” cosmetic
- “list_outcomes” reworded the description of “platform” cosmetic
- “list_outcomes” reworded the description of “since” cosmetic
- “list_webhook_deliveries” reworded the description of “api_key” cosmetic
- “list_webhook_deliveries” reworded the description of “limit” cosmetic
- “list_webhook_deliveries” reworded the description of “offset” cosmetic
- “list_webhook_deliveries” reworded the description of “status” cosmetic
- “redrive_webhook_delivery” reworded the description of “api_key” cosmetic
- “redrive_webhook_delivery” reworded the description of “delivery_id” cosmetic
- “remix_hook” reworded the description of “api_key” cosmetic
- “remix_hook” reworded the description of “count” cosmetic
- “remix_hook” reworded the description of “idempotency_key” cosmetic
- “remix_hook” reworded the description of “tags” cosmetic
- “remix_hook” reworded the description of “target_archetype” cosmetic
- “remix_hook” reworded the description of “text” cosmetic
- “remix_hook” reworded the description of “verbosity” cosmetic
- “report_outcome” reworded the description of “api_key” cosmetic
- “report_outcome” reworded the description of “hook_id” cosmetic
- “report_outcome” reworded the description of “idempotency_key” cosmetic
- “report_outcome” reworded the description of “likes” cosmetic
- “report_outcome” reworded the description of “platform” cosmetic
- “report_outcome” reworded the description of “posted_at” cosmetic
- “report_outcome” reworded the description of “retention_pct” cosmetic
- “report_outcome” reworded the description of “url” cosmetic
- “report_outcome” reworded the description of “views” cosmetic
- “revoke_key” reworded the description of “api_key” cosmetic
- “revoke_key” reworded the description of “prefix” cosmetic
- “score_hook” reworded the description of “api_key” cosmetic
- “score_hook” reworded the description of “idempotency_key” cosmetic
- “score_hook” reworded the description of “platform” cosmetic
- “score_hook” reworded the description of “tags” cosmetic
- “score_hook” reworded the description of “text” cosmetic
- “score_hook” reworded the description of “verbosity” cosmetic
- “score_hooks_batch” reworded the description of “api_key” cosmetic
- “score_hooks_batch” reworded the description of “compare” cosmetic
- “score_hooks_batch” reworded the description of “idempotency_key” cosmetic
- “score_hooks_batch” reworded the description of “platform” cosmetic
- “score_hooks_batch” reworded the description of “sets” cosmetic
- “score_hooks_batch” reworded the description of “tags” cosmetic
- “score_hooks_batch” reworded the description of “texts” cosmetic
- “score_hooks_batch” reworded the description of “verbosity” cosmetic
- “set_webhook” reworded the description of “api_key” cosmetic
- “set_webhook” reworded the description of “url” cosmetic
- “signup” reworded the description of “email” cosmetic
- “signup” reworded the description of “idempotency_key” cosmetic
- “signup” reworded the description of “name” cosmetic
- “start_generate_job” reworded the description of “api_key” cosmetic
- “start_generate_job” reworded the description of “archetypes” cosmetic
- “start_generate_job” reworded the description of “count” cosmetic
- “start_generate_job” reworded the description of “idempotency_key” cosmetic
- “start_generate_job” reworded the description of “mode” cosmetic
- “start_generate_job” reworded the description of “platform” cosmetic
- “start_generate_job” reworded the description of “style” cosmetic
- “start_generate_job” reworded the description of “tags” cosmetic
- “start_generate_job” reworded the description of “topic” cosmetic
- “start_generate_job” reworded the description of “topics” cosmetic
- “remix_hook” made “target_archetype” optional cosmetic
- “remix_hook” made “text” optional cosmetic
- 1 Aug 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 17 to 20. That category is still filling its 30-day observation window: 5 days of observed history at the previous scan, 6 at this one. The score rises as the window fills, whether or not the server changes.
- 31 Jul 26 +4
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 29 Jul 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.
- 28 Jul 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.
- 27 Jul 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 26 Jul 26 52
First indexed and scored.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 3 Aug 2026 · Probed https://engine-production-3bdc.up.railway.app/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=*.up.railway.app | CN=YE1,O=Let's Encrypt,C=US | 29 Jul 2026 | 27 Oct 2026 | ECDSA 256 | ECDSA-SHA384 | 6da79bb561da3efeb0e751ca21abd3999fe |
| SANs: *.up.railway.app, up.railway.app | ||||||
| CN=YE1,O=Let's Encrypt,C=US (CA) | CN=Root YE,O=ISRG,C=US | 3 Sept 2025 | 2 Sept 2028 | ECDSA 384 | ECDSA-SHA384 | 5ddd70dd31f801c85c186a7a04b80afe |
| CN=Root YE,O=ISRG,C=US (CA) | CN=ISRG Root X2,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | ECDSA-SHA384 | 872165fc34b6e5fba8add5b3705fb53a |
| CN=ISRG Root X2,O=Internet Security Research Group,C=US (CA) | CN=ISRG Root X1,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | SHA256-RSA | 6c8f1dc727c7117f7baf853ac980f9cd |
DNSSEC insecure
Validation of engine-production-3bdc.up.railway.app. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | trust_anchor | 20326, 38696 | 8, 8 | Verified |
| app. | present | 23684 | 8 | Verified |
| railway.app. | absent | Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation |
Authentication No authorisation required
The endpoint answered without asking for a token. Anyone who knows the URL can reach it.
| Result | No authorisation required |
|---|---|
| HTTP status | 200 |
| Header | Value |
|---|---|
| strict-transport-security | max-age=31536000 |
| x-content-type-options | nosniff |
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://engine-production-3bdc.up.railway.app/mcp | Verified | 200 | |
| http (plaintext) | http://engine-production-3bdc.up.railway.app/mcp | HTTPS enforced | 301 | https://engine-production-3bdc.up.railway.app/mcp |
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability.
add_credits ~353
Top up your credit balance (1-10000). Self-serve by default. When VHGENGINE_ADMIN_KEY is configured on the deployment this requires a matching admin_key argument; otherwise it stays self-serve (unless VHGENGINE_FREE_CREDITS is off). Idempotent on idempotency_key (replay does NOT grant twice). Returns {credits (new balance), granted}. Errors: unauthorized (missing/wrong admin key or self-serve disabled), invalid_request (amount range / balance ceiling), idempotency_conflict, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| admin_key | — | — | The deployment's VHGENGINE_ADMIN_KEY. Required ONLY when the operator configured one; omit on a self-serve deployment. A wrong or missing value where one is configured is unauthorized. |
| amount | integer | yes | Credits to add, 1-10000. Charged operations spend these; see pricing for the per-mode cost. The grant also has a per-account balance ceiling, so a large top-up on a full balance is invalid_request. |
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| Name | Type | Req | Description |
|---|---|---|---|
| credits | number|null | — | Balance AFTER the grant. |
| granted | number|null | — | Credits added by this call; 0 on an idempotent replay. |
| replayed | boolean|null | — | true when an earlier call with the same idempotency_key already granted. |
No examples provided.
cancel_job ~242
Cancel a job that is STILL QUEUED. Free, repeatable, never refunds. ALWAYS branch on `cancelled`; reason + next_action say what to do. A queued job was never charged (the worker charges), so credits_refunded is always 0. A RUNNING job cannot be stopped: it finishes, CHARGES and persists. Cancelled reads as status "failed" with error.error.details.cancelled true; to re-run, resubmit with a FRESH idempotency_key (reusing the cancelled one replays the cancelled result). Errors: unauthorized, forbidden (key lacks the spend scope), not_found, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| job_id | string | yes | The job to cancel. Account-scoped: an id you do not own reads as not_found, exactly like get_job. |
| Name | Type | Req | Description |
|---|---|---|---|
| already_cancelled | boolean|null | — | true when an earlier cancel_job had already cancelled this job, so this call was a no-op rather than a miss. |
| cancelled | boolean|null | — | true only when this call moved a still-QUEUED job to terminal. false means nothing changed; `reason` says why and `next_action` says what to do. |
| credits_refunded | number|null | — | Always 0. A queued job was never charged (the charge runs inside the worker), and a running job cannot be stopped, so a cancel never refunds. |
| error | — | — | Not returned by this tool. Named here because get_job(job_id) afterwards carries error.error.details.cancelled true (code 'conflict', details.reason 'cancelled'): THAT is how you tell your own cancel… |
| job_id | string|null | — | The job the cancel targeted. |
| next_action | string|null | — | The exact next call to make, if any. |
| reason | string|null | — | Why the call ended the way it did, in one sentence. |
| status | string|null | — | The job's status AFTER the attempt: 'failed' when cancelled, else whatever it really is (running / succeeded / failed). |
No examples provided.
create_checkout ~298
Get a Stripe hosted-checkout link for a credit pack. You never touch a card (WP-PAY). Hand the returned checkout_url to your human; the webhook credits you after they pay. Args: pack (a credits amount from pricing.credit_packs), success_url/cancel_url (optional), api_key (spend scope). Returns {checkout_url, pack, credits, usd_cents, expires_at}. Errors: unauthorized, forbidden, invalid_request (bad pack), payments_disabled (503; use add_credits), rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| cancel_url | — | — | Where Stripe sends the browser if your human abandons checkout. At most 2048 characters. Omit to use the deployment's default. |
| pack | integer | yes | Which credit pack to buy, given as its CREDITS amount and matched against pricing.credit_packs exactly (not a dollar figure and not an index). A value that is not an offered pack is invalid_request. |
| success_url | — | — | Where Stripe sends the browser after a successful payment. At most 2048 characters. Omit to use the deployment's default landing page. |
| Name | Type | Req | Description |
|---|---|---|---|
| checkout_url | string|null | — | The URL your human opens to pay. Single use. |
| credits | number|null | — | Credits that land on your balance after the webhook confirms payment. |
| expires_at | string|null | — | When the checkout link stops working, ISO-8601 UTC. |
| pack | number|null | — | Which pack this session buys. |
| usd_cents | number|null | — | What your human is charged, in cents. |
No examples provided.
create_key ~335
Mint a new named API key; the plaintext is returned ONCE. Requires admin scope. Delegate safely (WP-SCOPE): optional scopes (subset of read|spend|admin; omit for full power) + daily_credit_cap (credits/day; omit for uncapped) hand a sub-agent a key that can only do what you allow. Args: name (1-100), api_key (an admin-scoped key). Returns {api_key (store it), prefix, name, scopes, daily_credit_cap, created_at}. Errors: unauthorized, forbidden, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| daily_credit_cap | — | — | Credits this key may spend per UTC day, 1-100000; further charges on it are refused once reached (other keys are unaffected). Omit for uncapped. list_keys reports spent_today against this. |
| name | string | — | Label for the new key, 1-100 chars, shown by list_keys so you can tell delegated keys apart. Defaults to 'key'. |
| scopes | — | — | Powers the new key gets: read (free reads), spend (charged generate/score/remix), admin (key + account + webhook management). OMIT for a full-power key; pass a subset to hand a sub-agent strictly les… |
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | string|null | — | The plaintext key. Store it now; it is never readable again. |
| created_at | string|null | — | Creation time, ISO-8601 UTC. |
| daily_credit_cap | number|null | — | Per-UTC-day spend ceiling, or null for uncapped. |
| name | string|null | — | The label you gave it. |
| prefix | string|null | — | First 12 chars, used by list_keys / revoke_key / get_usage(key_prefix=...). |
| scopes | array|null | — | Powers granted, a subset of read/spend/admin (all three = full power). |
No examples provided.
delete_account ~242
Permanently delete this account. TERMINAL, not reversible. Admin scope (WP-LIFE). Pass confirm="delete" (guards an accidental call). Revokes ALL your keys, deletes your hooks, webhook, deliveries, jobs and idempotency rows, KEEPS the immutable financial ledger, and ANONYMIZES your outcomes into the shared corpus. Take what you still want FIRST: export_usage returns the ledger, every hook and every outcome in ONE call (get_usage / list_hooks / list_outcomes are the paged alternative). Errors: unauthorized, forbidden, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| confirm | string | yes | Must be exactly the string "delete". A guard: any other value (or omitting it) fails argument validation before anything is touched, so an accidental invocation cannot destroy the account. |
| Name | Type | Req | Description |
|---|---|---|---|
| deleted | boolean|null | — | true once the account is tombstoned. Every key now 401s. |
| note | string|null | — | Human-readable summary of what was and was not removed. |
| retained | object|null | — | {financial_events, anonymized_outcomes}: the immutable ledger is kept and your outcomes are anonymized into the shared corpus rather than destroyed. |
No examples provided.
delete_webhook ~117
Remove this account's webhook (WP-M). Admin scope. Args: api_key (admin scope). Cost=free. Returns {deleted: true}. Errors: unauthorized, forbidden, not_found (none registered), rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| Name | Type | Req | Description |
|---|---|---|---|
| deleted | boolean|null | — | true once removed. No further events are delivered. |
No examples provided.
export_usage ~291
Export EVERYTHING this account owns in ONE body: ledger, hooks, outcomes. Free. The take-it-with-you call; make it BEFORE delete_account. Same body as GET /v1/usage/export?format=json (csv is a download shape with no MCP carrier and its columns are a subset of `events`). UNBOUNDED, no paging: on a big account prefer get_usage / list_hooks / list_outcomes. `email` is masked without the admin scope. Returns {account_id, email, count, events, hooks, outcomes}; tag/key_prefix narrow events. Errors: unauthorized, forbidden (key lacks the read scope), rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| key_prefix | — | — | Narrow the LEDGER to charges made with ONE api key, identified by the 12-char prefix from list_keys. `hooks` and `outcomes` are always the full set and are NOT narrowed by this. Omit for every key. |
| tag | — | — | Filter to rows carrying EXACTLY this fleet tag (exact match, not a substring). Omit for every tag. |
| Name | Type | Req | Description |
|---|---|---|---|
| account_id | string|null | — | Your account id. |
| count | number|null | — | How many ledger rows `events` carries. |
| string|null | — | Contact address given at signup, MASKED (z***@d***.com) unless the calling key has the admin scope. Null when none was given. | |
| events | array|null | — | The FULL charge ledger, oldest first, narrowed by tag/key_prefix. Each {id, created_at, operation, credits_delta, balance_after, request_id, key_prefix, metadata}. This is the superset of the REST cs… |
| hooks | array|null | — | EVERY hook this account bought: {hook_id, request_id, topic, mode, platform, text, archetype, claim_type, score_total, score, contains_placeholder_stat, prompt_version, tags, created_at}. NOT narrowe… |
| outcomes | array|null | — | EVERY outcome this account reported, each with the hook snapshot taken at report time. NOT narrowed by tag/key_prefix. |
No examples provided.
generate_hooks ~800
Generate ranked, scored viral hooks for a topic. Charged by mode. Cost = base + per_hook * hooks_returned (instant 0+1, smart 0+2, research 10+4, ~50 at count=10); a pre-flight gate never bills a broke account. Deterministic only in instant; a progressToken streams stages; the hook id is `hooks[].id`. CANCELLING DOES NOT REFUND: a timeout cannot stop the engine thread, so the call still charges and persists. Send idempotency_key, or recover via list_hooks. Errors: unauthorized, invalid_request, llm_unavailable, insufficient_credits.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| archetypes | — | — | Restrict generation to these archetype ids (see list_archetypes). Omit to let the engine spread across archetypes, which is what you want unless you are deliberately narrowing a deck. |
| count | integer | — | How many hooks to generate, 1-25. Drives the price (base + per_hook * hooks_RETURNED) and the pre-flight reservation, so a high count on research is the expensive combination. |
| deadline_ms | — | — | Milliseconds you are willing to block, 1000-600000. If the chosen mode's p90 exceeds it the call returns IMMEDIATELY with auto_job:true + a job_id to poll with get_job instead of generating inline. O… |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| mode | — | — | Generation engine and therefore the price: instant (0 base + 1/hook, deterministic templates, sub-second), smart (0 + 2/hook, one LLM call, seconds), research (10 + 4/hook, brief->draft->judge, tens… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| style | — | — | Voice/tone to match, <=200 chars. Honored as a real instruction by smart and research; on instant it only varies which deterministic template fillers are drawn, so it cannot change the voice there. O… |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| topic | string | yes | What the hooks are about, 3-200 chars. A concrete subject ('cold plunges for desk workers') scores far better than a bare noun; on research it is also what the brief is researched against. |
| verbosity | string | — | How much of the response envelope to return: minimal (ids/text/totals only), standard (the default), full (adds per-dimension score attribution and timings). Shapes the RESPONSE only, never what is g… |
| Name | Type | Req | Description |
|---|---|---|---|
| auto_job | boolean|null | — | Present and true when deadline_ms made this return a JOB instead of hooks. CHECK IT BEFORE READING `hooks`: on this path `hooks` is ABSENT and the body is the job pointer below (job_id, status, estim… |
| base_price | number|null | — | Fixed part of the charge for this mode. |
| count_requested | number|null | — | The count you asked for, echoed so you never have to diff an array length against your own request. |
| credits_charged | number|null | — | Credits this call actually cost. |
| credits_remaining | number|null | — | Your balance AFTER this charge. |
| degraded_reason | string|null | — | verbosity=minimal ONLY, and only when non-null: hoisted out of `research` so the trimmed envelope still says WHY the run degraded. At standard/full read research.degraded_reason instead. |
| degraded_stages | array|null | — | verbosity=minimal ONLY, and only when non-empty: hoisted out of `research`; which stages to blame. At standard/full read research.degraded_stages. |
| engine | string|null | — | Which generator implementation produced the deck. |
| estimated_seconds | number|null | — | auto_job path only: queue-aware estimate of total time to a result. |
| expected_wait | object|null | — | {mode, p50_ms, p90_ms, source} for this mode, to size the NEXT call. |
| expires_at | string|null | — | auto_job path only: when an unexecuted job is abandoned, ISO-8601 UTC. |
| grounding_refund | object|null | — | Non-null ONLY when you asked for research and the VENDOR REFUSED the web-search tool: the full pipeline still ran, so you are not billed as smart, but part of the research premium is waived. {credits… |
| hint | string|null | — | auto_job path only: what to do next, in one sentence. |
| hooks | array|null | — | Ranked hooks, best first. Each is {id, text, archetype, score, claim_type, rank}. NOTE the key is `id`, and THAT is the hook id you pass to report_outcome(hook_id=...) / get_hook (where the same valu… |
| job_id | string|null | — | auto_job path only: poll it with get_job or block on wait_for_job. |
| judge_fallback_reason | string|null | — | smart/research only: why the judge did NOT rank this set, or null when it ran. Non-null means these score.totals are heuristic-scale, so never compare them against a judged run's totals. |
| llm_fallback | boolean|null | — | true when the requested LLM tier was unavailable and a cheaper engine ran instead; llm_fallback_reason says why. You are billed for what RAN. |
| llm_fallback_reason | string|null | — | Why the fallback happened, or null. |
| mode_requested | string|null | — | The mode string you sent, before alias/auto resolution. |
| mode_used | string|null | — | The engine that actually ran (instant|smart|research) after aliases and auto were resolved. May differ from mode_requested. |
| poll_after_seconds | number|null | — | auto_job path only: wait at least this long before the first get_job. |
| price_per_hook | number|null | — | Per-hook part of the charge for this mode. |
| pricing_mode | string|null | — | The tier billed, which is what price_per_hook belongs to. |
| prompt_version | string|null | — | Prompt build used, for reproducibility. |
| rank_basis | string|null | — | verbosity=minimal ONLY, alongside degraded_stages: hoisted out of `research`; what the served ORDER means (llm_judge | judge_order_no_scores | heuristic | unranked). At standard/full read research.ra… |
| reason | string|null | — | instant only, non-null when the template pool ran out: prose saying hooks were WITHHELD rather than duplicated. On a clause-topic it says outright that widening `archetypes` will not help, so do not… |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. |
| replayed_at_charge | boolean|null | — | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. |
| request_id | string|null | — | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. |
| requeued | boolean|null | — | auto_job path only: true when an idempotent resubmit revived an existing job. |
| research | — | — | mode research ONLY (null on instant/smart). The grounding ledger you paid for. Keys, and what to DO with each: - brief_summary (str), angles (list of {angle, detail, why_it_stops_scroll}), vocabulary… |
| score_disclaimer | string|null | — | The honest limits of the scores above. Absent at verbosity=minimal. |
| shortfall | object|null | — | {requested, returned, cause} on EVERY response. `cause` is a closed vocabulary: 'none' on a full delivery, else template_pool / model_under_delivery / pool_exhausted. Branch on cause; never retry on… |
| status | string|null | — | auto_job path only: the job's lifecycle state ('queued'). |
| status_url | string|null | — | auto_job path only: REST URL for the same status (HTTP clients). |
| timing | object|null | — | What actually happened: {latency_ms, stages}. `stages` keys are mode specific (instant render_ms; smart llm_ms/judge_ms; research brief_ms/draft_ms/judge_ms). |
| usage | object|null | — | LLM token usage {input_tokens, output_tokens, est_cost_usd} (+ web_searches and fallback_* counters when those paths ran). NULL means NO LLM ran, which is not the same as zero tokens. |
No examples provided.
generate_hooks_batch ~592
Generate hooks for many topics in ONE call, charged all-or-nothing. One transaction: over-balance charges nothing; each item bills base + per_hook * hooks_returned for the engine that ran (divergent tiers -> pricing_mode "mixed"). One mode + idempotency_key + tags cover the batch; a progressToken streams item i/n; hook ids are results[i].hooks[j].id. CANCELLING DOES NOT REFUND: a timeout cannot stop the engine thread, so the batch still charges and persists. Send idempotency_key. Errors: unauthorized, invalid_request, llm_unavailable, insufficient_credits.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| mode | — | — | Generation engine and therefore the price: instant (0 base + 1/hook, deterministic templates, sub-second), smart (0 + 2/hook, one LLM call, seconds), research (10 + 4/hook, brief->draft->judge, tens… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| requests | array | yes | 1-20 items, each {topic, count?, archetypes?, style?}. Per-item `mode` is rejected: mode, tags, verbosity and idempotency_key are set ONCE for the whole batch. Unknown item keys are invalid_request. |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| verbosity | string | — | How much of the response envelope to return: minimal (ids/text/totals only), standard (the default), full (adds per-dimension score attribution and timings). Shapes the RESPONSE only, never what is g… |
| Name | Type | Req | Description |
|---|---|---|---|
| base_price | number|null | — | Fixed part of the charge, or null when pricing_mode is mixed. |
| credits_charged | number|null | — | Credits this call actually cost. |
| credits_remaining | number|null | — | Your balance AFTER this charge. |
| degraded_any | boolean|null | — | Wider signal than judge_fallback_any: true when any item ran off-tier for any reason, including a partial stage that still delivered a full hook count. |
| degraded_reason | string|null | — | verbosity=minimal ONLY, and only when non-null: hoisted out of `research` so the trimmed envelope still says WHY the run degraded. At standard/full read research.degraded_reason instead. |
| degraded_stages | array|null | — | verbosity=minimal ONLY, and only when non-empty: hoisted out of `research`; which stages to blame. At standard/full read research.degraded_stages. |
| engine | string|null | — | The generator implementation, or 'mixed' when items diverged. |
| expected_wait | object|null | — | {mode, p50_ms, p90_ms, source} for the batch's tier. |
| judge_fallback_any | boolean|null | — | true when at least one item's hooks are HEURISTIC-scale because its judge did not rank them. Then results[i].judge_fallback_reason says which, and you must not rank topics by comparing avg(score.tota… |
| llm_fallback | boolean|null | — | true when ANY item fell back to a cheaper engine or a fallback provider. |
| llm_fallback_reason | string|null | — | Why the first such fallback happened, or null. |
| mode_requested | string|null | — | The mode string you sent, before alias/auto resolution. |
| mode_used | string|null | — | The engine that ran, after aliases and auto were resolved; 'mixed' when items diverged. |
| price_per_hook | number|null | — | Per-hook part of the charge, or null when pricing_mode is mixed. |
| pricing_mode | string|null | — | The tier billed across the batch, or 'mixed' when items fell back to different engines. On 'mixed', base_price/price_per_hook are null and each results[i] carries its own. |
| prompt_version | string|null | — | Prompt build used, for reproducibility. |
| rank_basis | string|null | — | verbosity=minimal ONLY, alongside degraded_stages: hoisted out of `research`; what the served ORDER means (llm_judge | judge_order_no_scores | heuristic | unranked). At standard/full read research.ra… |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. |
| replayed_at_charge | boolean|null | — | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. |
| request_id | string|null | — | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. |
| results | array|null | — | One entry per item, in request order: {topic, hooks, pricing_mode, price_per_hook, base_price, credits, grounding_refund, timing, judge_fallback_reason, count_requested, shortfall, reason, research}.… |
| score_disclaimer | string|null | — | The honest limits of the scores above. Absent at verbosity=minimal. |
| timing | object|null | — | {latency_ms} for the whole batch. |
No examples provided.
get_account ~175
Return this account's state + remaining rate-limit budget. Free read. `rate_limit` carries {limit, remaining, reset_epoch, reset_at, window_seconds} for the per-account window, the same budget REST clients read from X-RateLimit-* headers. Pace a fleet off `remaining` instead of discovering the ceiling by taking a rate_limited mid-run; this read itself consumes one of those calls. No API key is ever echoed back. Errors: unauthorized, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| Name | Type | Req | Description |
|---|---|---|---|
| account_id | string|null | — | Your account id. |
| api_key_prefix | string|null | — | Prefix of the key that authenticated this call. Never the key itself. |
| created_at | string|null | — | Account creation time, ISO-8601 UTC. |
| credits | number|null | — | Current balance. |
| string|null | — | Contact address if one was given at signup. | |
| name | string|null | — | Display name given at signup. |
| rate_limit | object|null | — | {limit, remaining, reset_epoch, reset_at, window_seconds} for the per-account window. Pace off `remaining`; limit 0 means limiting is disabled and remaining/reset are null. |
No examples provided.
get_activity ~215
See what this account's agents are doing: in-flight ops + recent ops. Free. `in_flight` merges the live-ops registry (real stage/pct/eta mid-run) with your queued/running jobs; `recent` is the last completed charged ops. Every row has a human-readable message. The REST surface also offers an SSE feed at GET /v1/activity/stream. Args: recent_limit (1-100, default 20), api_key. Errors: unauthorized, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| recent_limit | integer | — | Max completed rows in `recent`, 1-100. Does not limit `in_flight`, which always shows everything currently running. |
| Name | Type | Req | Description |
|---|---|---|---|
| in_flight | array|null | — | Live operations: the running-ops registry (real stage/pct/eta) merged with your queued and running jobs. Every row has a human-readable message. |
| recent | array|null | — | The last completed charged operations, sized by recent_limit. |
No examples provided.
get_estimates ~73
The measured/expected wait per generation mode. Free, no auth. Size a call before spending. Returns {op, modes:{instant|smart|research: {p50_ms, p90_ms, samples, source ("measured" once enough samples, else "default"), advice}}}.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| modes | object|null | — | Per mode {p50_ms, p90_ms, samples, source ('measured' once enough samples exist, else 'default'), advice}. |
| op | string|null | — | Which operation these estimates describe. |
No examples provided.
get_hook ~246
Fetch one bought hook in full, including its parsed score. Free (WP-G). Account-scoped: a foreign or unknown id is not_found (no existence leak). Args: hook_id (from a generate/batch/remix response or list_hooks), api_key. Returns {hook_id, text, archetype, claim_type, mode, platform, topic, score_total, score, prompt_version, request_id, created_at, outcomes:[...], outcome_summary:{count, max_views, avg_views}}. Report results with report_outcome. Errors: unauthorized, not_found, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| hook_id | string | yes | The hook's id, as returned in the `hooks[].hook_id` of a generate/batch/remix response or by list_hooks. Account-scoped: an id you do not own reads as not_found. |
| Name | Type | Req | Description |
|---|---|---|---|
| archetype | string|null | — | Archetype it was written in. |
| claim_type | string|null | — | What kind of claim it makes. |
| contains_placeholder_stat | boolean|null | — | true when the text carries an unverified number you must replace before posting (e.g. '90% of people'). Treat as an edit-before-use flag. |
| created_at | string|null | — | When it was generated, ISO-8601 UTC. |
| hook_id | string|null | — | The hook's id. |
| mode | string|null | — | Engine that produced it (instant|smart|research|remix). |
| outcome_summary | object|null | — | {count, max_views, avg_views} over those outcomes. |
| outcomes | array|null | — | Outcomes you have reported against this hook. |
| platform | string|null | — | Platform it was written for. |
| prompt_version | string|null | — | Prompt build that produced it, for reproducibility. |
| request_id | string|null | — | The generate call that bought it. |
| score | object|null | — | Per-dimension score breakdown. |
| score_total | number|null | — | Total craft score. |
| text | string|null | — | The hook line. |
| topic | string|null | — | Topic it was generated from. |
No examples provided.
get_job ~231
Poll an async job: status, real engine stage, progress, ETA, result/error. Poll after poll_after_seconds until status is "succeeded" (result holds the full generate body) or "failed" (error holds the typed envelope). Only the owning account can read a job; a foreign/unknown id is not_found (no existence leak). Returns {job_id, status, stage, progress_pct, eta_seconds, elapsed_ms, poll_after_seconds, result, error}. Errors: unauthorized, not_found, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| job_id | string | yes | The job's id, as returned by start_generate_job (or by a generate_hooks call that auto-jobbed on deadline_ms). Account-scoped: an id you do not own reads as not_found. |
| Name | Type | Req | Description |
|---|---|---|---|
| elapsed_ms | number|null | — | Milliseconds since the job started running. |
| error | object|null | — | On failure: the same typed error envelope a synchronous call returns. details.cancelled true means YOU cancelled it with cancel_job, not a fault. |
| eta_seconds | number|null | — | Estimated seconds remaining. |
| job_id | string|null | — | The job polled. |
| poll_after_seconds | number|null | — | How long to wait before polling again. |
| progress_pct | number|null | — | 0-100 progress within the run. |
| result | object|null | — | On success: the FULL generate (or batch) body, always full-verbosity. |
| stage | string|null | — | The real engine stage while running (e.g. brief, draft, judge). |
| status | string|null | — | queued (waiting on a worker), running, succeeded (see `result`), failed (see `error`). Stop polling on the last two. |
No examples provided.
get_usage ~425
Return the credit balance, per-operation totals, and recent ledger. Free. Args: recent_limit (1-200, default 50), offset (>=0, pages `recent` past the newest rows), request_id (scope recent to that call's charges), tag + key_prefix (WP-J fleet filters), api_key. Returns {credits, totals:{by_operation}, recent:[...]} (each row carries key_prefix; tags in metadata). Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| key_prefix | — | — | Filter to charges made with ONE api key, identified by the 12-char prefix from list_keys (e.g. vhg_sk_ab12). Omit for every key. |
| offset | integer | — | Rows to skip before `recent` starts, for paging past the newest page. Page with offset += recent_limit, exactly like list_hooks and list_outcomes; an offset past the end is an empty `recent`, never a… |
| recent_limit | integer | — | Max recent ledger rows to return, 1-200. Totals are unaffected by this; it only sizes `recent`. |
| request_id | — | — | Scope `recent` to the charges of ONE earlier call: pass the request_id that call RETURNED (or its X-Request-Id header), never a freshly minted id. An id that charged nothing matches no rows and comes… |
| tag | — | — | Filter to rows carrying EXACTLY this fleet tag (exact match, not a substring). Omit for every tag. |
| Name | Type | Req | Description |
|---|---|---|---|
| credits | number|null | — | Current balance. |
| recent | array|null | — | Recent charge rows, newest first, sized by recent_limit. Each carries the operation, credits, key_prefix, request_id and tags in metadata. |
| totals | object|null | — | {by_operation: {op: {calls, credits}}} over the whole account lifetime. |
| unreported_hooks | object|null | — | How many bought hooks still have no outcome, i.e. how much free reward is on the table. Find them with list_hooks(unreported=true). |
No examples provided.
get_webhook ~133
This account's webhook (url + last delivery status; never the secret). Free read. Args: api_key (read scope). Cost=free. Returns {url, created_at, last_delivery_status, last_delivery_at, events}. Errors: unauthorized, not_found (none registered), rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| Name | Type | Req | Description |
|---|---|---|---|
| created_at | string|null | — | Registration time, ISO-8601 UTC. |
| events | array|null | — | Event types being delivered. |
| last_delivery_at | string|null | — | When that attempt happened, ISO-8601 UTC. |
| last_delivery_status | string|null | — | Status of the most recent delivery attempt. |
| url | string|null | — | The registered endpoint. |
No examples provided.
getting_started ~77
The 5-step agent quickstart: modes, wait guidance, links. Free, no auth. Written to be parsed and acted on. Returns {what_this_is, five_steps, modes (cost + live latency + when to use), wait_guidance (expected_wait, estimates, progressToken, jobs), links}.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| data_and_deletion | string|null | — | Retention and what delete_account keeps. |
| evaluate | object|null | — | How to judge output quality, including the public bake-off. |
| five_steps | array|null | — | The 5 ordered steps from nothing to hooks. |
| fleet_accounting | string|null | — | How tags and per-key caps attribute spend across a fleet. |
| links | object|null | — | Docs and endpoint links. |
| memory_card | string|null | — | A compact block worth persisting into your own memory. |
| modes | array|null | — | One entry PER MODE (a list, not a map): {mode, base, per_hook, formula, p50_ms, p90_ms, latency_source, when_to_use}. |
| payments | string|null | — | How to top up: add_credits and create_checkout. |
| response_shaping | string|null | — | How verbosity changes what you get back. |
| wait_guidance | object|null | — | How to avoid blocking blind: expected_wait, progressToken, jobs. |
| webhooks | string|null | — | How to receive job and low-balance events. |
| what_this_is | string|null | — | One-paragraph description of the service. |
No examples provided.
health ~144
Deep health: DB read+write probe, worker/queue, backup + integrity. Free. Same db.write_probe/backup.status/backup.offsite_status/integrity.status calls GET /health makes, so the two can never disagree. status is "ok" only when the DB both reads AND writes. Returns {status, version, time, llm_configured, outcomes_reported_total, job_worker_alive, job_queue_depth, last_backup_at, backup_count, offsite, integrity}. llm_configured never calls an LLM; CHECK job_worker_alive before start_generate_job (false: a queued job never runs).
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| backup_count | number|null | — | How many daily backups are retained. Null when status is unavailable. |
| integrity | object|null | — | Weekly PRAGMA quick_check result: {last_check_at, ok}, both null before the first check has ever run. Null (the whole object) when status is unavailable. |
| job_queue_depth | number|null | — | Jobs queued and not yet claimed, across all accounts. Null when status is unavailable. |
| job_worker_alive | boolean|null | — | Whether this deployment runs the job worker. FALSE means start_generate_job would queue a job nothing executes: use generate_hooks instead. |
| last_backup_at | string|null | — | Newest nightly backup's timestamp, ISO-8601 UTC. Null before the first backup runs, or when status is unavailable. |
| llm_configured | boolean|null | — | Whether an LLM key is configured. false means smart/research are unavailable and mode=auto resolves to instant. A pure config read, never a live call. |
| offsite | object|null | — | Off-volume upload health: {enabled, last_upload_at, last_status, remote_retained}. In-memory, no live S3 call. Null when status is unavailable. |
| outcomes_reported_total | number|null | — | Size of the shared outcome corpus (60s-cached COUNT). Null when status is unavailable. |
| status | string|null | — | 'ok' only when the DB both READS and WRITES (a write probe, not just a ping); 'unavailable' otherwise. |
| time | string|null | — | Server time, ISO-8601 UTC. |
| version | string|null | — | Server version. |
No examples provided.
list_archetypes ~77
List the hook archetypes with psychology, platforms, and templates. Free, no auth. Returns {archetypes:[{id, name, description, psychological_trigger, best_for, templates}]}. Use an id for generate_hooks(archetypes=[...]) or remix_hook(target_archetype=...).
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| archetypes | array|null | — | Each {id, name, description, psychological_trigger, best_for, templates}. Use `id` for generate_hooks(archetypes=[...]) / remix_hook(target_archetype=...). |
No examples provided.
list_billing_events ~251
Recent billing events (usage.recorded, credits.granted, credits.low). Free. Newest first, paged with limit/offset like list_hooks and list_outcomes. Returns {events:[{id, event_type, payload, created_at}], limit, offset, total}. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| limit | integer | — | Max billing events to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| Name | Type | Req | Description |
|---|---|---|---|
| events | array|null | — | This page: {id, event_type, payload, created_at}. |
| limit | number|null | — | Page size actually applied. |
| offset | number|null | — | Offset this page started at. |
| total | number|null | — | Rows matching the filters IGNORING paging. |
No examples provided.
list_hooks ~589
List the hooks this account has bought, newest first. Free (WP-G). Persisted hooks stay retrievable for 90 days. Args: mode (instant|smart|research| remix, or any generate alias such as template/search), since (ISO timestamp), tag (exact fleet-tag match), topic (substring), unreported (true: only hooks a first outcome report can still reward), limit (1-100), offset, api_key. Returns {hooks:[{hook_id, text, archetype, mode, score_total, created_at, request_id}], limit, offset, total}. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| limit | integer | — | Max hooks to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| mode | — | — | Filter to hooks bought from this engine. Canonical stored values: instant|smart|research|remix; the generate aliases (template|off|quick, llm|on|fast, search|deep|deep_research) are accepted and reso… |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| since | — | — | Return only rows created at or after this UTC timestamp. Compared LEXICALLY against stored 'YYYY-MM-DDTHH:MM:SSZ' values, so pass that exact format (a date-only string or a unix epoch silently select… |
| tag | — | — | Filter to hooks carrying EXACTLY this fleet tag: a lowercase slug of 1-40 chars of [a-z0-9_-], matched LITERALLY. `_` is a legal tag character and is NOT a wildcard here, and neither is `%`; there is… |
| topic | — | — | Case-insensitive SUBSTRING match against the topic a hook was generated for (unlike `tag`, which is exact). Omit for every topic. |
| unreported | — | — | true = only hooks with no outcome yet, i.e. the ones a first report_outcome can still earn a reward on. false = only hooks already reported. Omit for both. |
| Name | Type | Req | Description |
|---|---|---|---|
| hooks | array|null | — | This page: {hook_id, text, archetype, mode, score_total, created_at, request_id}. hook_id is what report_outcome and get_hook take. |
| limit | number|null | — | Page size actually applied. |
| offset | number|null | — | Offset this page started at. |
| total | number|null | — | Rows matching the filters IGNORING paging: the number to page through, not the number returned here. |
No examples provided.
list_jobs ~254
List this account's jobs, newest first (summaries without the result blob). Args: limit (1-200, default 50), offset (>=0), api_key. Returns {jobs:[{job_id, status, stage, progress_pct, created_at, started_at, finished_at}], limit, offset, total}. Errors: unauthorized, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| limit | integer | — | Max jobs to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| Name | Type | Req | Description |
|---|---|---|---|
| jobs | array|null | — | Summaries: {job_id, status, stage, progress_pct, created_at, started_at, finished_at}. Fetch a result with get_job. |
| limit | number|null | — | Page size actually applied. |
| offset | number|null | — | Offset this page started at. |
| total | number|null | — | Jobs matching ignoring paging. |
No examples provided.
list_keys ~170
List this account's API keys as PREFIXES only (never the raw key). Admin scope. Returns {keys:[{prefix, name, scopes, daily_credit_cap, spent_today, created_at, revoked_at}]}, oldest first; revoked_at is null for an active key, scopes lists the key's grant (WP-SCOPE), spent_today is its credits spent since UTC midnight. Your signup key shows as name "default". Errors: unauthorized, forbidden, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| Name | Type | Req | Description |
|---|---|---|---|
| keys | array|null | — | Oldest first. Each {prefix, name, scopes, daily_credit_cap, spent_today, created_at, revoked_at}; revoked_at null means still active, and the signup key shows as name 'default'. |
No examples provided.
list_outcomes ~430
List the posted outcomes THIS account has reported, newest first. Free (WP-LIFE). Retrieve your own submitted telemetry in bulk (per-hook it rides get_hook). Args: platform (tiktok|instagram|youtube|x|other), since (ISO timestamp), hook_id (one hook), limit (1-200, default 50), offset (>=0), api_key. Each row carries the outcome plus its snapshot (hook_text, topic, mode, archetype, claim_type, score_total, score_source, prompt_version, tags). Returns {outcomes:[...], limit, offset, total}. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| hook_id | — | — | Filter to the outcomes reported against ONE hook (its id from list_hooks / a generate response). Omit for every hook. |
| limit | integer | — | Max outcomes to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| platform | — | — | Filter to outcomes reported for one platform. Omit for all. |
| since | — | — | Return only rows created at or after this UTC timestamp. Compared LEXICALLY against stored 'YYYY-MM-DDTHH:MM:SSZ' values, so pass that exact format (a date-only string or a unix epoch silently select… |
| Name | Type | Req | Description |
|---|---|---|---|
| limit | number|null | — | Page size actually applied. |
| offset | number|null | — | Offset this page started at. |
| outcomes | array|null | — | Each row is the reported outcome plus a snapshot of the hook at report time (hook_text, topic, mode, archetype, claim_type, score_total, score_source, prompt_version, tags). |
| total | number|null | — | Rows matching the filters ignoring paging. |
No examples provided.
list_runs ~459
Every call this account was charged for, newest first. Free read. Recover a lost response in two calls, never re-charged: list_runs(request_id=...) for the receipt, then follow `hooks_url` for the hooks it produced. Page with `cursor` (one pass total) or `offset`; stop only when `exhausted` is true, never on a short page. Same composed read GET /v1/runs makes, so the two can never disagree. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| charged_only | boolean | — | true (default): only calls that COST credits. false: also include grants and zero-cost calls. |
| cursor | integer | — | Ledger position to resume scanning from (see `next_cursor` on a prior page); not a run count, not an opaque token. Leave at 0 and follow `next_cursor` to walk your whole history in bounded reads. |
| limit | integer | — | Max runs to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| operation | — | — | Return only runs of this operation: generate_hooks, generate_hooks_batch, remix_hook, score_hook, score_hooks_batch, add_credits. An unknown value returns an empty page, never an error. |
| request_id | — | — | Return only the run with this X-Request-Id (the response header on every call, including errors). This is the lookup for a lost response whose id you still have. |
| Name | Type | Req | Description |
|---|---|---|---|
| credits_remaining | number|null | — | Your balance right now. |
| exhausted | boolean|null | — | true only when the read reached the END of your ledger. `total` is the COMPLETE count only when this is true. |
| has_more | boolean|null | — | true when this page is not the whole remainder. Follow `next_cursor` (preferred) or `next_offset`; never infer 'that was all' from a short page. |
| limit | number|null | — | Page size actually applied. |
| next_cursor | number|null | — | Pass as `cursor` for the next page (leave offset at 0); null when nothing follows. PREFER this over next_offset for a full walk: it resumes exactly where this page stopped instead of re-scanning from… |
| next_offset | number|null | — | Pass as `offset` for the next page; null when nothing follows. |
| offset | number|null | — | Offset this page started at. |
| runs | array|null | — | This page: {request_id, operation, created_at, credits_delta, credits_charged, balance_after, key_prefix, topic, topics, platform, mode, hooks_returned, tags, hooks_url, usage_url, metadata}. hooks_u… |
| scan_limit | number|null | — | Ledger rows one page may examine, however many reads that takes. |
| scanned | number|null | — | Ledger rows examined to build this page. |
| total | number|null | — | Rows matching the filters WITHIN the scanned window (see `scanned`), not over all history unless `exhausted` is also true. |
No examples provided.
list_webhook_deliveries ~383
List this account's webhook deliveries, newest first. Free read (WP-HOOKS). Verify your receiver end-to-end (a webhook.test ping is enqueued at registration) and diagnose failures without waiting out a real event. Args: status (pending|retrying| delivered|dead|retired), limit (1-200, default 50), offset (>=0), api_key. Each row has {delivery_id, event_type, status, attempts, last_status_code, error, timestamps, next_attempt_at, payload_preview (200 chars; the full body is never returned)}. Dead rows are kept 7 days. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| limit | integer | — | Max deliveries to return, 1-200. Above the ceiling is an invalid_request, never a silent truncation. |
| offset | integer | — | Number of rows to skip for paging, 0-9223372036854775807. Page with offset += the limit you actually requested; `total` in the response is the unpaged count. The ceiling is SQLite's largest bindable… |
| status | — | — | Filter to deliveries in this state: pending (queued, not yet attempted), retrying (failed, backing off), delivered (2xx), dead (retries exhausted; redrive_webhook_delivery can requeue it), retired. O… |
| Name | Type | Req | Description |
|---|---|---|---|
| deliveries | array|null | — | Each {delivery_id, event_type, status, attempts, last_status_code, error, timestamps, next_attempt_at, payload_preview (200 chars; the full body is never returned)}. `dead` rows are kept 7 days and c… |
| limit | number|null | — | Page size actually applied. |
| offset | number|null | — | Offset this page started at. |
| total | number|null | — | Deliveries matching the filter ignoring paging. |
No examples provided.
pricing ~121
The machine-readable price list, with per-mode expected_wait. Free, no auth. Returns {unit, usd_per_credit (0 while credits are free), operations, pricing_modes:{instant:{base:0,per_hook:1},smart:{base:0,per_hook:2}, research:{base:10,per_hook:4}} each with a formula, expected_wait, signup_grant, low_balance_threshold}. A generate charge is base + per_hook * hooks_returned; these are the exact constants the charge path uses.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| credit_packs | array|null | — | Buyable packs; pass one of these credit amounts as create_checkout(pack=...). |
| expected_wait | object|null | — | Per-mode {p50_ms, p90_ms, source} latency, same as get_estimates. |
| low_balance_threshold | number|null | — | Balance at which the credits.low webhook fires. |
| operations | object|null | — | Per-operation costs for the non-generate tools. |
| payments_enabled | boolean|null | — | false when Stripe is not configured: create_checkout then returns payments_disabled and add_credits is the only top-up path. |
| pricing_modes | object|null | — | Per mode {base, per_hook, formula, expected_wait}. A generate charge is base + per_hook * hooks_RETURNED. |
| signup_grant | number|null | — | Credits a new account starts with. |
| unit | string|null | — | Name of the billing unit (credits). |
| usd_per_credit | number|null | — | Cash price per credit; 0 while credits are free. |
No examples provided.
quote ~455
Price and time a generate BEFORE you commit to it, without spending. Free. Answers what you cannot otherwise learn without paying: which engine will BILL (mode=auto resolves here), credits_max, expected_wait, and whether this key can afford it now (balance AND any daily account/key cap, spelled out in `blocker`). Same validation as generate_hooks, so a bad topic/archetype/mode fails HERE. credits_max is a CEILING (you pay per hook RETURNED) and nothing is reserved. Errors: unauthorized, invalid_request, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| archetypes | — | — | Restrict generation to these archetype ids (see list_archetypes). Omit to let the engine spread across archetypes, which is what you want unless you are deliberately narrowing a deck. |
| count | integer | — | How many hooks you intend to ask for, 1-25. Drives the quoted ceiling: credits_max = base + per_hook * count. |
| mode | — | — | Generation engine and therefore the price: instant (0 base + 1/hook, deterministic templates, sub-second), smart (0 + 2/hook, one LLM call, seconds), research (10 + 4/hook, brief->draft->judge, tens… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| topic | string | yes | The topic you intend to generate for, 3-200 chars. It is fully VALIDATED here, so a bad topic is an invalid_request now instead of after you commit to spending. |
| Name | Type | Req | Description |
|---|---|---|---|
| affordable | boolean|null | — | true when a generate with these arguments would pass every pre-flight gate (balance AND any daily account/key spend cap). Branch on THIS. |
| base_price | number|null | — | Fixed part of the charge for pricing_mode. |
| blocker | — | — | Null when affordable. Otherwise the SAME typed error block generate_hooks would have returned ({code, message, retriable, details, ...}), whose details.hint names the exact fix. |
| count | number|null | — | The hook count quoted. |
| credits_max | number|null | — | The CEILING: base_price + price_per_hook * count. The real charge bills hooks RETURNED, so a short deck costs less. Never more than this. |
| credits_remaining | number|null | — | Your balance right now. |
| credits_short | number|null | — | How many credits you are missing; 0 when affordable. Against a daily cap this is the gap to the remaining daily headroom, not to your balance. |
| expected_wait | object|null | — | {mode, p50_ms, p90_ms, source} for pricing_mode. Compare p90_ms against your own patience to choose sync vs start_generate_job + wait_for_job. |
| formula | string|null | — | The exact charge formula, so you can predict any other count. |
| llm_configured | boolean|null | — | Whether this deployment has an LLM key. false means smart/research are unavailable and mode=auto resolves to instant. |
| mode_requested | string|null | — | The mode string you passed, before resolution; null if omitted. |
| operation | string|null | — | The operation this quote prices ('generate_hooks'). |
| platform | string|null | — | The platform quoted. |
| price_per_hook | number|null | — | Per-hook part of the charge for pricing_mode. |
| pricing_mode | string|null | — | The tier that will actually be BILLED after aliases and auto were resolved: instant|smart|research. This, not mode_requested, is what the price belongs to. |
| recommendation | string|null | — | One sentence naming the next call to make. |
| topic | string|null | — | The topic as validated (echoed back). |
No examples provided.
redrive_webhook_delivery ~240
Requeue a dead-lettered webhook delivery: reset to pending, due now. Admin scope. Valid ONLY on a `dead` delivery (a live receiver that exhausted its retries); any other status is a 409 conflict and a delivery you do not own is not_found. It re-attempts through the normal pipeline and, if it dies again, dead-letters normally. Args: delivery_id (from list_webhook_deliveries), api_key. Returns the refreshed row. Errors: unauthorized, forbidden, not_found, conflict, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| delivery_id | string | yes | The delivery to requeue, from list_webhook_deliveries. Only a `dead` row can be redriven; any other status is a conflict and a delivery you do not own is not_found. |
| Name | Type | Req | Description |
|---|---|---|---|
| attempts | number|null | — | Attempts so far; the redrive adds to this count. |
| created_at | string|null | — | When the delivery was first enqueued, ISO-8601 UTC. |
| dead_at | string|null | — | When it was dead-lettered, or null once redriven. |
| delivered_at | string|null | — | When it finally succeeded, or null. |
| delivery_id | string|null | — | The delivery that was requeued. |
| error | string|null | — | Last failure reason, or null. |
| event_type | string|null | — | The event this delivery carries. |
| last_status_code | number|null | — | HTTP status of the most recent attempt, or null. |
| next_attempt_at | string|null | — | When the pipeline will try again, ISO-8601 UTC. Null on a terminal row. |
| payload_preview | string|null | — | First 200 chars of the body; the full body is never returned. |
| status | string|null | — | Reset to 'pending' and due now. |
No examples provided.
remix_hook ~860
Rewrite an existing hook into a target archetype. 2 credits per variant. Args: EXACTLY one of text or hook_id; target_archetype OR its alias archetype (disagreeing spellings are a 400); count, tags, verbosity, api_key, idempotency_key. Deterministic; billed only for variants returned. Returns {original:{text, score}, remixes:[{id, text, archetype, score, rank}], topic_core, reason, score_disclaimer, credits_charged, credits_remaining, request_id}. Errors: unauthorized, invalid_request, not_found, insufficient_credits, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| archetype | — | — | Alias for `target_archetype`, spelled the way every hook object in every response spells it. Send either one; sending both is fine only if they are equal, and a disagreement is a typed invalid_reques… |
| count | integer | — | How many variants to produce, 1-5, at 2 credits each. You are billed only for variants actually returned. |
| hook_id | — | — | Remix a hook you ALREADY bought, by the id a generate/batch/remix response returned (also listed by list_hooks): the stored text is looked up for you, so you do not have to carry it back. Account-sco… |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| platform | — | — | The platform whose SCORING WINDOW grades these variants, as on generate_hooks and score_hook: LinkedIn's ideal hook length is 10-18 words against 8-14 elsewhere, so it moves both score.total and the… |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| target_archetype | — | — | Archetype id to rewrite INTO (see list_archetypes for ids and their psychology). `archetype` is an accepted alias for this argument; one of the two is required, there is no default angle. |
| text | — | — | The existing hook to rewrite, 3-300 chars. Its subject is kept; only the angle moves to target_archetype. Pass this OR `hook_id`, never both and never neither. |
| topic | — | — | The subject the hook was written FOR, scored as on score_hook: the verbatim-echo penalty only fires when the scorer is told the topic, so a hook (or a variant) that repeats its own subject scores up… |
| verbosity | string | — | How much of the response envelope to return: minimal (ids/text/totals only), standard (the default), full (adds per-dimension score attribution and timings). Shapes the RESPONSE only, never what is g… |
| Name | Type | Req | Description |
|---|---|---|---|
| credits_charged | number|null | — | Credits this call actually cost. |
| credits_remaining | number|null | — | Your balance AFTER this charge. |
| llm_fallback | boolean|null | — | true when the LLM remixer was unavailable and the deterministic one ran; llm_fallback_reason says why. You are billed for what RAN. |
| llm_fallback_reason | string|null | — | Why the fallback happened, or null. |
| original | object|null | — | {text, score} of what you passed in. |
| platform | string|null | — | The platform scoring window every total above was produced on. Score a variant through score_hook with this SAME platform to reproduce its number; a different window gives a different total and a dif… |
| reason | string|null | — | Why these rewrites fit the target archetype, or null. |
| remix_engine | string|null | — | Which remixer produced the variants. |
| remixes | array|null | — | The variants: {id, text, archetype, score, rank}. Billed per variant RETURNED, so this can be shorter than count. |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. |
| replayed_at_charge | boolean|null | — | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. |
| request_id | string|null | — | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. |
| score_disclaimer | string|null | — | The honest limits of the score attached above. |
| topic_core | string|null | — | The subject the remixer preserved from the original. |
No examples provided.
report_outcome ~516
Report what a bought hook actually did once posted. FREE (WP-H). Honest telemetry feeding the outcome corpus (no view prediction claimed). Args: hook_id, platform (tiktok|instagram|youtube|x|other), posted_at (ISO, not >48h future), views/likes (0..1e11), retention_pct? (0-100), url? (http(s)+host), api_key, idempotency_key. Caps 20/hook, 500/day; an exact duplicate is a conflict. Returns {outcome, aggregate, reward_credits, credits_remaining?}. Errors: unauthorized, not_found, invalid_request, conflict, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| hook_id | string | yes | Which bought hook this result belongs to: the hook_id from a generate/batch/remix response or list_hooks. Must be a hook you own. |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| likes | — | — | Likes at report time, >=0. Omit if you cannot read it. |
| platform | string | yes | Where the hook was actually posted. Required. |
| posted_at | string | yes | When it went live, ISO-8601 UTC ('YYYY-MM-DDTHH:MM:SSZ'). May be in the past; more than 48h in the FUTURE is invalid_request. |
| retention_pct | — | — | Average view-through as a PERCENT, 0-100 (not a 0-1 fraction). Omit if the platform does not expose it. |
| url | — | — | Public http(s) URL of the post, for your own audit trail. Must carry a host; omit entirely rather than passing a placeholder. |
| views | integer | yes | Views the post had accrued at report time, >=0. Required: it is the only field the corpus calibrates against. Report again later (up to 20 reports per hook) to record how it matured. |
| Name | Type | Req | Description |
|---|---|---|---|
| aggregate | object|null | — | Rolled-up totals for this hook across every report you have made. |
| credits_remaining | number|null | — | Your balance after the reward. Present only when one was granted. |
| outcome | object|null | — | The stored row, as recorded. |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored report. |
| reward_credits | number|null | — | Credits granted for this report. Only a hook's FIRST report earns one, so later reports return 0. |
No examples provided.
revoke_key ~238
Revoke the key with `prefix` (from list_keys): it stops working, others keep working. Use to kill a leaked or old key. Args: prefix (the 12-char key prefix, e.g. vhg_sk_ab12), api_key (a DIFFERENT active key to authenticate this call). Returns {prefix, name, revoked, revoked_at, already_revoked}. You cannot revoke your LAST active key (create a replacement first). Requires the admin scope. Errors: unauthorized, forbidden, invalid_request (last key), not_found, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| prefix | string | yes | The 12-char key prefix to kill, copied from list_keys (e.g. vhg_sk_ab12), NOT the plaintext key. Revoking is idempotent; you cannot revoke your last active key. |
| Name | Type | Req | Description |
|---|---|---|---|
| already_revoked | boolean|null | — | true when it was already revoked, i.e. this call changed nothing. |
| name | string|null | — | Its label. |
| prefix | string|null | — | The key that was revoked. |
| revoked | boolean|null | — | Whether the key is now revoked (true after a successful call). |
| revoked_at | string|null | — | When it was revoked, ISO-8601 UTC. |
No examples provided.
score_hook ~598
Score any hook text on 5 dimensions with rewrite tips. 1 credit. Deterministic heuristic scorer (no LLM). Args: text (3-300), platform, topic (optional, reproduces generate's score via the verbatim-echo penalty), tags (1-5 fleet slugs, WP-J), verbosity (full keeps per-dimension attribution), api_key, idempotency_key (replay not re-charged). Returns {score:{...,total}, verdict, suggestions, confidence, disclaimer, credits_charged, credits_remaining, request_id}. Errors: unauthorized, invalid_request, insufficient_credits, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| text | string | yes | The hook line to score, 3-300 chars. Any text is accepted (it does not have to be one VHGENGINE generated) and it is never persisted as a hook you own. |
| topic | — | — | The subject this text was written FOR. Pass the SAME topic string a generate call used and this reproduces the exact score.total that generate served for the hook: the scorer's verbatim-echo penalty… |
| verbosity | string | — | How much of the response envelope to return: minimal (ids/text/totals only), standard (the default), full (adds per-dimension score attribution and timings). Shapes the RESPONSE only, never what is g… |
| Name | Type | Req | Description |
|---|---|---|---|
| confidence | string|null | — | How much weight to put on this score. |
| credits_charged | number|null | — | Credits this call actually cost. |
| credits_remaining | number|null | — | Your balance AFTER this charge. |
| disclaimer | string|null | — | The honest limits of the scorer. Worth surfacing to your human. |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. |
| replayed_at_charge | boolean|null | — | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. |
| request_id | string|null | — | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. |
| score | object|null | — | Per-dimension scores plus `total`. |
| suggestions | array|null | — | Concrete rewrite tips. |
| verdict | string|null | — | One-line read of the total. |
No examples provided.