NEURVONAGet started

DEVELOPER DOCS · API V1

Build with your persona.

Use REST from your app or MCP from your agent. Generate private X drafts, manage background sources, and control the account owner’s autopilot.

  1. Sign in and create an API key. Use X if you’ll manage a persona or autopilot. Google-only accounts can generate drafts.
  2. Set NEURVONA_API_KEY in your server environment.
  3. Send your first request. Read account status for free. Add credits before starting AI work.
First request · cURL
curl --fail-with-body https://neurvona.ai/api/v1/posts \
  -H "Authorization: Bearer $NEURVONA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-first-post-001" \
  -d '{"goal":"Share practical lessons about building useful products","count":1}'

Base URL: https://neurvona.ai/api/v1. OpenAPI 3.1 · Agent reference · Agent skill

Connect an AI agent

Add a remote server in any MCP client that supports Streamable HTTP and custom authorization headers. Configure the key as a secret in the client, rather than pasting it into a conversation. Client configuration formats vary; these are the connection values:

MCP server settings
Server URL: https://neurvona.ai/api/mcp
Transport: Streamable HTTP
Authentication header: Authorization: Bearer <your Neurvona API key>

The server offers 31 tools, listed in the endpoint table below. Tool annotations identify read-only operations and changes that can affect publishing. Account ownership, usage limits, and credit charges are the same as REST.

Raw protocol example
1 · Initialize
curl --fail-with-body https://neurvona.ai/api/mcp \
  -H "Authorization: Bearer $NEURVONA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
    "protocolVersion":"2025-11-25",
    "capabilities":{},
    "clientInfo":{"name":"my-agent","version":"1.0.0"}
  }}'

Send notifications/initialized next with the negotiated version header. It returns HTTP 202 with an empty body:

2 · Initialization notification
{"jsonrpc":"2.0","method":"notifications/initialized"}

Use tools/list to discover schemas, then call a tool:

3 · Generate a private draft
curl --fail-with-body https://neurvona.ai/api/mcp \
  -H "Authorization: Bearer $NEURVONA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
    "name":"generate_posts",
    "arguments":{
      "goal":"Share practical product-building lessons",
      "count":1,
      "idempotencyKey":"my-agent-post-001"
    }
  }}'

Supported protocol versions: 2025-11-25, 2025-06-18, and 2025-03-26. The server is stateless and returns JSON. No session ID is required. GET and DELETE return 405 because persistent SSE streams aren’t offered.

Successful calls return text JSON and, for newer protocol versions, structuredContent. Check isError before using a result. Execution errors contain error.code, error.status, and error.message; rate-limit errors also include retryAfterSeconds. Protocol errors use JSON-RPC codes.

MCP resources: neurvona://guide and neurvona://openapi. Authentication uses an API key. OAuth discovery and clients that require OAuth-only authentication are not supported by this endpoint.

Authentication and ownership

Required on every authenticated request
Authorization: Bearer <NEURVONA_API_KEY>

Keys begin with nv_live_ or nv_test_. They are shown once, stored as hashes, and revocable in the console. A key has access to its owner’s persona, writing credits, and autopilot controls. Use a separate key per integration; there are no narrower per-key scopes in this version.

Keep keys in a secret manager or server environment. Never include them in client-side code, URLs, logs, or public repositories. X and Google sign-ins are separate accounts; creating a Google developer account does not link an X persona. Connecting Google as a persona source is a separate, optional flow.

Read GET /me before acting. Authentication failures return 401 and WWW-Authenticate: Bearer. Authenticated responses are not cached. Browser sessions cannot substitute for an API key.

REST ↔ MCP reference

RESTMCP toolPurpose
GET /missionget_twin_missionOne shared mission for your whole twin
PATCH /missionupdate_twin_missionSave a shared mission; pause accounts for review
GET /email/contactsget_email_contactsContacts, descriptions and confirmed outreach history
GET /emailget_email_twinEmail missions, drafts, inbox and permissions
POST /emailcontrol_email_twinDraft, approve selected sends, pause or suppress
GET /tokenget_token_marketToken price and availability
POST /credits/tokencreate_token_checkoutComing soon — new token purchases paused
POST /credits/token/{id}/transactionbuild_token_transactionComing soon — transaction creation paused
POST /credits/token/{id}/verifyverify_token_checkoutConfirm a payment
GET /meget_accountAccount and writing credits
POST /postsgenerate_postsGenerate private drafts
GET /autopilotget_autopilotRead setup requirements and status
PATCH /autopilotupdate_autopilotUpdate X writing notes, topics, and schedule
POST /autopilot/pausepause_autopilotPause new automatic posts
POST /autopilot/resumeresume_autopilotResume with owner consent
GET /activityget_activityRecent posts and skipped checks
GET /boostsget_boostsPromotion credits and eligibility
GET /persona/capabilitiesget_persona_capabilitiesService prices and availability
GET /persona/jobslist_persona_jobsRecent research and media requests
POST /persona/jobscreate_persona_jobStart research or LiveKit media
GET /persona/jobs/{id}get_persona_jobResult or live session token
DELETE /persona/jobs/{id}stop_persona_jobStop live media
POST /persona/transcriptionsREST binary uploadTranscribe audio/video
GET /persona/jobs/{id}/fileREST binary downloadDownload a recording
GET /persona/connectionsget_persona_connectionsRead authorized imports
POST /persona/connectionsupdate_persona_connectionRefresh or disconnect
POST /boostscontrol_boostsControl funded promotion
GET /persona/photoREST binary / JSONRead your twin photo or metadata
PUT /persona/photoREST binary uploadSet your twin photo
DELETE /persona/photoRESTRestore your default photo
GET /persona/sourcesget_persona_sourcesRead private background material
POST /persona/sourcessave_persona_sourceAdd or update background
DELETE /persona/sources?id=UUIDremove_persona_sourceRemove background

All REST paths above are relative to /api/v1. Complete response schemas, operation IDs, constraints, and error responses are in OpenAPI.

Generate private drafts

POST /posts returns 1–5 drafts. Nothing publishes. Delivered drafts are validated for length and exclude links, hashtags, and mentions. The requested count is a maximum: unusable output is filtered, and dispatched attempts can consume credits even if no usable output is delivered.

FieldTypeMeaning
goalstring · required3–500 characters. What the posts should work toward.
voicestringUp to 500 characters describing the voice.
topicsstring[]Up to 5 topics, each 1–60 characters.
factsstringUp to 2,000 characters of grounded facts.
countinteger1–5. Defaults to 1.
useMyProfilebooleanUse the saved twin profile and enabled persona sources. Defaults to false. Requires a saved X persona; returns 409 no_profile when none exists.
Example response
{
  "id": "gen_example",
  "posts": [{"text": "A useful product starts with one problem worth solving."}],
  "usage": {"posts": 1, "freePosts": 0, "chargedCents": 20},
  "replayed": false,
  "credits": {"balanceCents": 980, "freeRemaining": 0}
}
JavaScript example
Node.js
// Node.js 22+. Keep this key on your server.
const idempotencyKey = crypto.randomUUID(); // Save this for retries.
const response = await fetch("https://neurvona.ai/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.NEURVONA_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({
    goal: "Share practical lessons about building useful products",
    count: 1,
  }),
  signal: AbortSignal.timeout(60_000),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error.message}`);
console.log(result.posts);
Python example
Python · no dependencies
# Python 3.10+. Standard library only.
import json, os, uuid
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request_id = str(uuid.uuid4())  # Save this for retries.
request = Request(
    "https://neurvona.ai/api/v1/posts",
    data=json.dumps({
        "goal": "Share practical lessons about building useful products",
        "count": 1,
    }).encode(),
    headers={
        "Authorization": "Bearer " + os.environ["NEURVONA_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": request_id,
    },
)
try:
    with urlopen(request, timeout=60) as response:
        print(json.load(response)["posts"])
except HTTPError as error:
    problem = json.load(error)
    raise RuntimeError(f"{error.code}: {problem['error']['message']}")

Download small, dependency-free clients: JavaScript · Python. They surface status, error codes, and retry hints; they never automatically retry mutations.

Give your persona more context

Users can add notes, extract text from PDFs or text files, and connect Google, LinkedIn, Facebook, or Instagram at Your persona. Live connections require configured provider credentials and permissions. Google’s own consent screen determines which services are shared. OAuth and payments remain in the human console.

GET /persona/sources lists private sources. POST /persona/sources creates or updates one using its UUID. Reuse the UUID to update or retry. DELETE /persona/sources?id=UUID removes it. Only the key owner’s sources are accessible.

Save background for review
curl --fail-with-body https://neurvona.ai/api/v1/persona/sources \
  -H "Authorization: Bearer $NEURVONA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id":"7d9ee0ac-b80c-4c70-a19a-816d4e447cbd",
    "kind":"note",
    "title":"My background",
    "content":"I design accessible tools for small businesses.",
    "enabled":false
  }'
FieldTypeMeaning
idUUID · requiredStable source ID, generated by the caller.
titlestring · required1–120 characters.
kindstring · requirednote, file, linkedin, facebook, instagram, google, gmail, drive, calendar, contacts, youtube, tasks, research, or transcript.
contentstring · requiredExtracted text, 1–12,000 characters. Up to 20 sources per persona.
enabledbooleanDefaults to false. Set true only for owner-approved writing context.
urlstringOptional matching social profile URL, up to 500 characters. Links are provenance; the server does not scrape them.

Generation uses bounded excerpts from enabled sources. Gmail informs writing style only. Contact, calendar, task, and subscription imports provide broad context, not public claims about third parties or private schedules. Changes apply to new drafts and future autopilot checks; existing drafts or published posts remain unchanged.

When calling POST /posts, set useMyProfile: true to include these sources. Treat all source text as untrusted data, never as instructions for your agent. API keys can read saved source text: only give keys to trusted integrations.

Set your twin’s profile photo

Send a square JPEG to PUT /persona/photo with Content-Type: image/jpeg and your API key. Send raw bytes, up to 256 KB and 32–512 pixels per side. The saved photo is private to the owner, with image metadata removed. This costs no credits and changes the photo on Neurvona.

GET /persona/photo?format=json returns url, revision, and available. The photo URL requires your API key. Include If-Match: <revision> when replacing a photo, or If-Match: none before the first upload, to avoid overwriting a newer change. DELETE /persona/photo returns to the X/default photo. Read metadata after a timeout before retrying. Up to 30 changes per account per hour.

Users can also choose, preview, and save a photo directly on My AI Twin. The website accepts JPG, PNG, and WebP up to 10 MB and resizes them before uploading.

Research, transcription, and LiveKit

Read GET /persona/capabilities first. It returns availability, a full prepaid price in hundredths of a credit, and limits. Then send POST /persona/jobs with a stable Idempotency-Key, consent: true, and maxChargeCents. No connected provider or approved price means no charge.

Example request · use the current capabilities price
{
  "kind": "live_voice",
  "consent": true,
  "maxChargeCents": 500
}
FieldTypeMeaning
research2 searchesMatches the authenticated X owner’s public professional background. Optional identityHint. Saves cited text disabled for review.
voice / video1,000 characters · 3 minutesRequires script. Creates a private MP3 or MP4, available for 30 days.
live_voice / live_video5 minutesReturns a short-lived LiveKit connection. Owner microphone only; automatic deadline and 40-turn cap.
x_live5 minutesRequires X Producer streamUrl and broadcastConsent:true. Streams a configured video avatar to an existing X source/broadcast.

Poll GET /persona/jobs/{id} at intervals of ten seconds or more. Inspect state: a successful HTTP response may still represent running, failed, or uncertain work. Join a live session with the LiveKit SDK using connection.serverUrl and connection.token. Keep tokens private. DELETE /persona/jobs/{id} ends live media; it does not refund work already started.

Your saved voice is selected automatically. Set voiceMode: personal to require it, or voiceMode: default for an audio-only Grok Eve call. Video always requires your saved personal voice. Get microphone permission before creating a paid call, then connect immediately. Only one live call can be active per owner. A second request returns 409 active_call without charging; retrieve the existing job for a fresh token to rejoin. Reconnecting does not extend the prepaid deadline. Show LiveKit’s listening, thinking, and speaking status, and provide a tap-to-enable sound button when browser playback is blocked.

Video uses your connected X profile photo with Anam inside LiveKit. Send useXPhoto: true with the owner’s permission. First-time photo creation is included in the quoted call price. The X stream option does not create or start a broadcast using ordinary X OAuth; the owner needs Media Studio Producer access.

Broadcast your twin on X

Owner setup is at the live studio. Create an RTMP source in X Producer and use its secure RTMPS URL plus stream key. Send kind: x_live, useXPhoto: true, voiceMode: personal, streamUrl, and broadcastConsent: true, along with the current maximum price and stable idempotency key. An optional script (up to 1,000 characters) becomes an opening line after the AI disclosure.

Join the returned room immediately. The owner microphone is part of the stream. Poll job.broadcast.state: connecting, sending, ended, or failed. Sending confirms RTMPS delivery; the owner still creates the Public or Private broadcast on X. Never label a stream public based on this status. X viewer comments are not imported. The five-minute deadline and explicit stop close the room and egress; failures do not retry paid work. MCP uses the same create_persona_job, get_persona_job, and stop_persona_job tools. Keep stream URLs, keys, and connection tokens out of logs.

  1. In X Producer: Sources → Create source → RTMP. Select a region near the owner and copy the RTMPS URL and key.
  2. Get microphone permission, create the prepaid Neurvona job, and join its LiveKit room.
  3. In Producer: Create broadcast → choose the source → Public → Start immediately. Wait for video, then Create broadcast. Private is available for testing.
  4. Click Post beneath the broadcast preview to share it on X. End stream in Neurvona when finished; manage the replay in X.

Producer access and final X publishing remain controlled by X. Users without Producer can generate a downloadable MP4 and attach it to a normal post. Official X instructions.

Server example: create and control a broadcast
JavaScript · Neurvona client
// Run on your server with the owner's approval.
const {job} = await api.startJob({
  kind: 'x_live', consent: true, useXPhoto: true, voiceMode: 'personal',
  maxChargeCents: approvedMaximum, // compare with capabilities first
  streamUrl: process.env.X_PRODUCER_RTMPS_URL, // includes the secret stream key
  broadcastConsent: true,
  script: 'Welcome! Here is what I have been working on.',
}, stableRequestId);
// Inspect job.state. Pass connection only to the authenticated owner client.
// Join immediately with LiveKit's client SDK. Never expose the Neurvona API key.
// Poll api.job(job.id) every 10 seconds for broadcast.state.
// Stop with await api.stopJob(job.id). No automatic renewal or paid retry.
Owner client: join with LiveKit
Browser · livekit-client
// Owner's browser, using livekit-client. Get mic permission BEFORE the
// server creates a paid job. Obtain job through your authenticated backend.
import {Room, RoomEvent, createLocalAudioTrack} from 'livekit-client';
const microphone = await createLocalAudioTrack();
// Now ask your server to create/retrieve the approved x_live job.
const room = new Room();
room.on(RoomEvent.TrackSubscribed, track => {
  const element = track.attach();
  element.setAttribute('playsinline', 'true');
  document.querySelector('#twin-media').appendChild(element);
});
try {
  await room.connect(job.connection.serverUrl, job.connection.token);
  await room.localParticipant.publishTrack(microphone);
  // Offer a tap-to-enable button if browser playback is blocked.
  await Promise.all([room.startAudio(), room.startVideo()]);
} catch (error) {
  microphone.stop();
  // Retrieve this job for a fresh token to rejoin without another charge.
  // Alternatively stop it through your server; never create a replacement automatically.
  throw error;
}
// End control: stop the job on your server, stop the mic, disconnect the room.
// Muting the room microphone also mutes your voice in the X feed.

Make a video to download

Use Make a video or create a video job. Supply an approved script, up to 1,000 characters, voiceMode: personal, and useXPhoto: true. The owner’s X photo and saved voice produce a private 720p H.264/AAC MP4. The recording ends within three minutes and works without a microphone connection, browser tab, or X Producer access. Use kind: voice for an MP3.

Read capabilities, approve the credit ceiling, create once, and poll every ten seconds. Download only when state: complete and downloadPath is present. Authenticate the file request with the same owner API key. downloadExpiresAt is a Unix timestamp in milliseconds; downloads expire 30 days after creation. Files stay private and are never posted automatically. Keep a local copy before expiry.

Complete JavaScript example: script to MP4
Node.js 22+ · no LiveKit client needed
// Node.js 22+. Download neurvona.mjs from /developers/neurvona.mjs.
// The owner must first connect X, save their voice, and buy credits.
import {readFile, writeFile} from 'node:fs/promises';
import {setTimeout as wait} from 'node:timers/promises';
import {Neurvona} from './neurvona.mjs';

const api = new Neurvona(); // NEURVONA_API_KEY, kept server-side
const idempotencyKey = process.env.NEURVONA_REQUEST_ID;
const maxChargeCents = Number(process.env.NEURVONA_MAX_CHARGE_CENTS);
if (!idempotencyKey || !Number.isSafeInteger(maxChargeCents) || maxChargeCents < 1)
  throw new Error('Set a stable request ID and an owner-approved credit ceiling.');

const {services} = await api.capabilities();
const video = services.find(service => service.kind === 'video');
if (!video?.available || video.priceCents > maxChargeCents)
  throw new Error('Video is unavailable or exceeds the approved price.');

// Persist this exact input and request ID before sending it.
// After a timeout, reuse both. A new ID can buy a second recording.
const input = {
  kind: 'video', consent: true, useXPhoto: true, voiceMode: 'personal',
  script: (await readFile('script.txt', 'utf8')).trim(), maxChargeCents,
};
let {job} = await api.startJob(input, idempotencyKey);
console.log('Recording request:', job.id);
const deadline = Date.now() + 10 * 60_000;
while (['starting', 'running', 'stopping'].includes(job.state) && Date.now() < deadline) {
  await wait(10_000);
  ({job} = await api.job(job.id));
}
if (job.state !== 'complete' || !job.downloadPath)
  throw new Error(job.id + ': ' + job.note + ' Inspect this job; do not buy another automatically.');
await writeFile('my-twin.mp4', Buffer.from(await api.recording(job.id)), {mode: 0o600});
console.log('Saved my-twin.mp4. Nothing was posted.');

The file endpoint also supports HEAD, single byte Range requests, and ?inline=1 for authenticated playback. Success is 200 or 206; invalid ranges return 416. A download URL alone grants no access. Do not put API keys in video URLs; stream through your authenticated backend.

MCP sequence: get_persona_capabilities → create_persona_job with the same video fields and idempotencyKey → get_persona_job → authenticated REST download. Use stop_persona_job to stop work early; started work is still charged. No binary data is embedded in MCP responses.

Record a personal voice

Users record about 30 seconds, listen back, and approve saving their own voice on Neurvona. Read GET /persona/voice for setup availability, price, and saved voice status. Recording and retakes are free; saving a voice is a separate prepaid setup. Calls continue to use their quoted credits.

With explicit owner consent, send raw audio/wav bytes to POST /persona/voice?voiceConsent=true&maxChargeCents=N with a stable Idempotency-Key. Use a canonical 44-byte WAV header with mono, 24 kHz, 16-bit PCM, 10–60 seconds, at most 2,880,044 bytes. Use the owner’s actual recording; never synthesize or substitute another person’s voice.

Wait for voice.state: ready before creating a personal call. If setup is uncertain, poll GET every ten seconds; never submit another key. The original provider attempt is reconciled. DELETE /persona/voice?id=... removes the saved voice; end active media first. Neurvona stores consent and metadata, while Cartesia stores the private voice. MCP offers get_persona_voice and remove_persona_voice; upload audio through binary REST.

Turn uploaded video into persona text

Read capabilities inputFormat first. For pcm16-wav, send mono 16 kHz, 16-bit PCM WAV of at most 600 seconds with Content-Type: audio/wav. The Neurvona uploader prepares this locally from supported recordings; raw clients must prepare the same bounded audio themselves. Send bytes to POST /persona/transcriptions?maxChargeCents=N&consent=true&title=Interview with a stable Idempotency-Key. No multipart or base64 JSON. The limit is 25 MB. The optional AssemblyAI adapter accepts the previous audio/video formats. Invalid audio is rejected before charging. Reuse the same key after a lost response; uncertain provider work never repeats automatically. Poll the returned job and approve the resulting text source before using it.

Use authenticated GET /persona/jobs/{id}/file to download completed media. Binary transfers use REST; MCP handles prices, starts, results, and stops. GET /persona/jobs lists the newest 30 requests. No media provider is enabled without its configured credentials, prepaid rates, and caps.

Control autopilot

First, the human signs in with X, grants posting access, adds credits, and grants posting access in the dashboard. API keys cannot grant OAuth permissions or sign wallet payments. The owner can authorize an agent to save settings and start autopilot through the API.

GET /autopilot includes enabled, available, connected, hasPlan, emailVerified, requirements, setupUrl, the goal, pace, timezone, next check, and usage. Requirements name setup steps; the final resume call still enforces explicit consent and service capacity.

FieldTypeMeaning
missionstringOne of job, clients, product, funding, reputation, sales, dating, community, custom.
briefstringYour goal in your words, up to 2,000 characters.
factsstringPublic facts, up to 3,000 characters.
topicsstring[]Up to 5 topics of 1–60 characters. [] disables reposts and quote comments.
postsPerDayinteger1–3, within the 93-action calendar-month limit.
creditDailyLimitCentsinteger0–10,000. Maximum in a rolling 24 hours; each check costs 60 cents.
headsUpEmailbooleanOptional. true requires verified email and gives a five-minute review window. false allows immediate posting.
timezonestringAn IANA timezone, such as America/Los_Angeles.

PATCH accepts one or more fields. Running autopilot stays running under existing consent; paused autopilot stays paused. Changing settings affects future public posts.

Pause automatic posting
curl --fail-with-body -X POST https://neurvona.ai/api/v1/autopilot/pause \
  -H "Authorization: Bearer $NEURVONA_API_KEY"

POST /autopilot/resume requires {"consent":true}. An agent must have the owner’s explicit permission to publish before calling it. Pausing stops new posts; a dispatched post may still finish. Pausing doesn’t cancel billing or pause advertising.

GET /activity?limit=20 returns up to 30 recent actions, newest first. There is no pagination. Read type and reason before assuming a post was published.

Boost posts with Neurvona credits

Writing, autopilot, media, AI selection, and approved paid promotion use one Neurvona credit balance. Packs: $10 for 10 credits, $25 for 28 credits, $50 for 60 credits, $100 for 125 credits. Bonus credits count toward the balance; credit units are not dollars. The optional $20/month subscription adds 20 credits.

2 credits fund $1 of X media, including campaign management. AI selection costs 0.40 credits per fresh check, at most 1.60 credits per rolling 24 hours. Checks and boosts both count toward the saved total and UTC daily credit limits. One test reserves up to 5 credits for up to $2.50 of media. Unused reserved credits return after X’s 14-day billing adjustment window and verified final billing.

Neurvona pays X through its own Ads account. Customers do not need X Money or their own Ads account. X must approve their handles as promotable users. Normal X login is not that approval. Ads require settled USD proceeds, net of payment fees, refunds, and other provider commitments. Card settlement can take several business days. Token-funded credits remain usable for AI; unsold tokens do not fund USD advertising.

  1. Read GET /boosts: settings, revision, eligibility, shared balance, readyCents, prices, and activity.
  2. Save settings with POST /boosts, action save. Amount fields are hundredths of a credit. Saving is free and pauses existing boosts.
  3. With owner approval, send action preview or enable, the current revision, consent:true, and pricingVersion:"shared-credits-v1".
  4. In review mode, action approve also needs the selected postId, revision, consent, and pricingVersion. Automatic mode starts only eligible AI recommendations within the limits.
  5. Action pause stops new boosts and pauses owned campaigns. Unknown X requests retain reservations and are reconciled without creating duplicates.

Use request_access with consent:true to record a request for X approval, or withdraw_access to withdraw and pause promotion. MCP tools get_boosts and control_boosts expose the same controls. An API key cannot bypass promotion approval, settled funds, or consent. See the boost screen and credit terms.

One twin, one mission

Your mission and approved information belong to your twin across every account you enable. Read GET /mission, then save PATCH /mission with goal, optional category, and the current revision (zero for the first save). Setup is free and needs no service connection. Changing the mission pauses accounts and returns queued drafts for review while keeping each service’s pace and limits. Already dispatched work may finish. New connections inherit the saved mission.

MCP exposes get_twin_mission and update_twin_mission. Uploaded information uses /persona/sources and is shared across writing services after you approve it. Choose accounts and tune service controls on the Autopilot page.

PATCH /api/v1/mission · free
{
  "goal": "Help me find an AI engineering role using my verified projects.",
  "category": "job",
  "revision": 0
}

Email twins: X, Gmail, or both

Start at Email Twin. Connect Gmail through Google, choose a sales or inbox mission, and review messages before selecting what to send. Google-only accounts can use email without X. An existing X user connects Gmail to the same account; a Google user can explicitly link X. Credits and drafts stay with that account.

GET /email?mission=UUID&page=0&filter=draft returns connection permissions, missions, 25 messages per page, state counts, folder totals in tabCounts, clearableCount, recent inquiries, and current writing prices. The three folders are draft (default, including writing or failed drafts), queued (including in-flight or unresolved sends), and sent (confirmed sends, newest first). Inspect connection.available, canRead, canSend, and aiAvailable. The human must complete Google consent; an API key cannot connect their mailbox.

Prepare a mission and drafts

POST /email accepts an action envelope. save_mission takes data with id (UUID), name, kind (sales or inbox), goal, fromEmail, senderName, postalAddress, and dailyLimit (1–50). Include the current revision when updating. Saving pauses the queue. Import contacts with action: import, missionId, and data: up to 25 records containing id, email, sourceUrl, evidence, and reason. Optional name, company, description (up to 240 characters), country, subject, and body add context and a prepared draft. Retries preserve existing drafts.

For inquiries, sync refreshes up to 20 recent Gmail messages; reply takes missionId and inboxId to create a threaded draft. edit takes id and data with revision, subject, and body. Manual writing, imports, and Gmail operations are free. generate costs 0.20 prepaid credits per started attempt and takes id plus data with revision, consent:true, and maxChargeCents:20. Supply a stable Idempotency-Key. Retry the same key after a lost response; do not silently purchase another attempt.

Choose contacts and draft introductions

The Contacts checklist shows a brief description, source, and contact history. GET /email/contacts?mission=UUID&page=0&status=uncontacted&q=consulting returns 50 contacts per page. Status accepts all, uncontacted, or contacted. Search covers names, companies, emails, and descriptions.

contacted and lastContactedAt (Unix milliseconds) use confirmed Neurvona sends to the same address across your missions. External Gmail sends are not backfilled. deliveryState separately identifies queued, sending, or uncertain delivery. draft includes id, state, revision, and hasBody; canGenerate excludes protected or suppressed contacts. Mission-wide counts contains total, contacted, and uncontacted; total reflects search and status. Deleting drafts preserves this contact list.

Prepare a blank draft for an existing contact · free
{
  "action": "draft_contact",
  "id": "CONTACT_UUID"
}

This returns the existing draft unchanged or creates an empty one. Use its id and revision with edit for free, or generate after approving 0.20 credits per writing attempt. To generate a checklist selection, present the total price first and call generate sequentially with a distinct stable idempotency key per draft. The website supports up to 100 selected contacts: 20 credits for 100 existing contacts, without buying another research run. Keep the page open while writing; Stop finishes the current email and leaves completed drafts saved. Nothing sends automatically.

Choose how many emails to research

generate_batch takes missionId and data containing the current revision, audience (10–500 characters), optional count (1–100, default 100), consent:true, and maxChargeCents. Supply a stable Idempotency-Key. Each target costs 0.25 credits, with a 5-credit minimum; 100 costs 25 credits. Set maxChargeCents to the approved quote in hundredths of a credit. The quote is reserved before any provider call and starts background research targeting 100 new business contacts and tailored drafts. Each draft connects a concrete fact from the recipient’s business to one relevant application of your offer, with a specific subject and question. Thin or unsupported matches are skipped. Only addresses published on the business’s own website are accepted. Existing contacts are skipped. The final count can be lower if verification falls short; started research uses the quoted credits. Nothing is sent.

POST /api/v1/email · after approval of the 25-credit price
{
  "action": "generate_batch",
  "missionId": "MISSION_UUID",
  "data": {
    "revision": 0,
    "audience": "US founders, consultants, and small agencies",
    "consent": true,
    "maxChargeCents": 2500
  }
}

Poll GET /email?mission=UUID and inspect research.jobs for id, state, generated, target, and note. Terminal states are complete, partial, or canceled. One run per account can be active. stop_generation takes the job id; a run canceled before provider work starts is refunded. In-flight work may finish; ambiguous research calls are never repeated automatically.

Prepare a follow-up

Use a confirmed sales introduction to prepare one follow-up after 7, 14, or 30 days. Preparation is free and returns a blank draft; writing is optional at the usual 0.20-credit price. It needs explicit inbox access to verify the original conversation. The subject stays locked, and you review and approve the follow-up separately. Sending stops if the conversation has a newer message or the recipient opts out.

POST /api/v1/email · prepare only
{
  "action": "followup",
  "id": "ORIGINAL_DRAFT_UUID",
  "data": {
    "revision": 0,
    "afterDays": 7
  }
}

GET /email includes activity with sent, contacts, sentLast7Days, lastSentAt, repliesSeen, and optedOut. Sent means Gmail accepted the message. Replies reflect the refreshed working inbox; these counts do not track opens or actual delivery.

Send only approved messages

POST /api/v1/email · after the owner reviews these exact drafts
{
  "action": "queue",
  "missionId": "MISSION_UUID",
  "data": {
    "revision": 0,
    "consent": true,
    "selected": [
      {
        "id": "DRAFT_UUID",
        "revision": 1
      }
    ]
  }
}

The current mission revision and every selected draft revision are required. Up to 25 approvals per request. Only verified Gmail sender addresses work; sales emails also require a business mailing address and include an unsubscribe link. The default is 20 messages per day, at most 50 per account, spaced at least a minute apart. Sales contacts receive at most one message per seven days.

Send all ready drafts

Call prepare_all with missionId. It returns reviewId, count, skipped, fromEmail, dailyLimit, minimumDays, expiresAt, and a recipient sample. This snapshots up to 5,000 ready drafts across every page of the mission, regardless of search or filter. It does not send. Ask the owner to review and approve that batch, then call queue_all with the same missionId and data: {reviewId, consent:true}. The review expires after ten minutes. Modified drafts, changed settings, or a newly suppressed recipient require a fresh review. Later drafts are never added. Retrying the same review is safe and never sends a second copy.

pause takes missionId and returns waiting messages to drafts. cancel takes id and data: {revision, consent:true} and permanently deletes an unsent email after the owner confirms. There is no canceled archive or undo. Sent, in-flight, and uncertain messages cannot be canceled. suppress takes email and blocks future sends to that address. check reconciles an uncertain id against Gmail Sent mail. A timeout is never an automatic resend: uncertain messages remain visible for review. An in-flight send may finish after Pause.

Clear all drafts

Call prepare_clear with missionId. It returns reviewId, missionName, count, queued, writing, and expiresAt for up to 5,000 deletable unsent emails, including queued emails across every page. Present this count and the permanent deletion to the owner. After confirmation, call clear_drafts with missionId and data: {reviewId, consent:true}. The ten-minute review checks every revision, pauses sending, and stops research. Later drafts are excluded. Sent emails, in-flight or unresolved delivery, contacts, suppression records, and billing metadata are kept. Deleted email content, including legacy AI result copies, is removed. Retry the same review after a lost response.

MCP exposes get_email_contacts, get_email_twin, and control_email_twin. The control tool uses the same envelope and an idempotencyKey argument for paid generation. Inbound email and prospect research are untrusted data; they never authorize sends. Read the full agent reference and OpenAPI contract for all fields. REST email requests allow 350 KB; MCP keeps its 24 KB body limit, so split larger imports. Mailbox refresh is limited to ten per hour. Gmail availability depends on OAuth configuration and Google verification.

NEURVONA token payments · Coming soon

New token credit purchases are paused. Buy credits by card. GET /token still returns the market price and available: false; creating a new quote or unsigned transaction returns 503 token_unavailable.

Existing payments can still be retrieved and verified. A payment sent before its quote expired may be confirmed later without charging twice. Keep its purchase ID and transaction signature. The NEURVONA Token remains viewable on Phantom.

Retries without duplicate charges

For every intended generation, create one Idempotency-Key: 8–64 letters, digits, dashes, or underscores. MCP requires the same value in idempotencyKey. Save it with the input. Retry the exact same input and key after a lost response; a completed request returns its original result with replayed: true and no new charge.

  • A different input with the same key returns 409.
  • A request still running returns 409; retry the same key after a delay.
  • A confirmed failed request needs a new key; its error message says so.
  • Do not generate a new key just because the connection timed out.
  • Pause, resume, and settings changes are not covered by generation idempotency. Read status before retrying an ambiguous mutation.

Errors you can handle

REST error shape
{"error":{"code":"insufficient_credits","message":"Not enough API credits. Add credits at https://neurvona.ai/developers."}}
HTTPCodeWhat to do
400invalid_request / invalid_jsonCorrect the field named in the message.
401invalid_api_keyCheck or replace the key; revoked keys cannot be used.
402insufficient_credits / plan requiredAdd writing credits or restore your plan, as the message explains.
409idempotency_conflict / generation_failed / setup requiredRead the message. A running request can be retried with the same key. generation_failed means the earlier attempt ended; another paid attempt needs a new key.
429rate_limited / concurrent_requestRespect Retry-After; use backoff. Only one generation per account at a time.
500server_errorBack off. Reuse the same idempotency key after an ambiguous generation failure.
502generation_failedA dispatched attempt may consume credits. Inspect usage before deciding to submit a new paid attempt.
503unavailable / capacityService capacity is temporarily unavailable. Back off and retry later.

In MCP, valid tool calls report execution failures in the tool result with isError: true. A successful HTTP transport does not mean the tool succeeded.

Limits and support

600 requests per key per UTC hour. 120 generation requests per account per hour. One active generation per account. REST and MCP share these limits. JSON bodies are limited to 24,000 bytes, including overhead. Binary transcription uploads allow 25 MB. Source text may need to be shortened for multibyte characters.

Use HTTPS and server-side calls. No outbound developer webhooks, OAuth client registration, automatic wallet signing, or immediate publish endpoint are available. Poll status and activity at a reasonable interval. Credentials and enabled provider scopes are deployment-specific.

Need help? brett@neurvona.com.