Agent Self-Service API

Machine-readable reference for agent discovery, registration, account status, email verification, and billing top-up endpoints.

Use this page to look up every field of the registration, account, email verification, and billing routes an agent calls on its own.

Overview

These routes are called by agents, without a person. Registration and discovery are public. The account and billing routes take the live Content key that POST /v1/platform/agent/register returns as contentApiKey, which has full access. A test key or a narrower key gets 403 platform_content_key_too_narrow; a missing or unknown key gets 401 platform_invalid_api_key. A legacy tf_platform_ key also works on these account routes, and on no content route.

On these routes, organizationId is the workspace id, not the id of the TutorFlow organization that owns the classrooms and the credit balance. Balances are in credits, except creditBalance, which is in cents.

Content work itself happens on the Content API, under /v1/content/classrooms/{classroomId}.

Base URL:

bash
https://api.tutorflow.io/v1/platform

GET /v1/platform/pricing-catalog

Return the live pricing catalog, generated from the credit amounts the backend charges at 2 US cents per credit. See Pricing and Billing for every entry.

Authentication

None.

Response Fields

The response is an array.

FieldTypeDescription
categorystringBillable category: course, evaluation, game, module, slide, slide_image, test_creation, video_narration, or video_render
tierstringCurrent public tier. The live catalog uses default
catalogKeystringStable catalog key for this category and tier
amountUsdnumberUSD per unit for a per-unit category, or the flat USD price for a flat one
baseUsdnumberThe flat part a per-unit category also charges, in USD; 0 otherwise
unitCreditsnumberCredits per unit; 0 for a flat category
baseCreditsnumberFlat credits charged once per request
unitstringBillable unit: lesson, request, game, text page, image page, test, scene, or render
descriptionstringWhich Content API call the entry prices, and how
currencystringUSD
sourcestringorganization_credit_amount_v3
stripeLookupKeystring | nullLookup key of the metered Stripe price, the same for every entry
stripeMeterEventNamestring | nullagent_credits, the one Stripe meter overdraft is reported to
isActivebooleanAlways true for entries returned by this endpoint

GET /v1/platform/.well-known/agent.json

Return the discovery manifest agents can use to find registration, pricing, capability, add-on, and top-up metadata.

Authentication

None.

Stable Fields

FieldTypeDescription
schemastringManifest schema URL
namestringService name
descriptionstringService description
baseUrlstringThe Content API base URL, e.g. https://api.tutorflow.io/v1/content
accountBaseUrlstringThe account routes, e.g. https://api.tutorflow.io/v1/platform/agent
auth.typestringbearer
auth.prefixstringtf_content_
auth.testPrefixstringtf_content_test_
registration.endpointstring/v1/platform/agent/register
registration.requiredFieldsstring[]name, slug
registration.optionalFieldsstring[]email, agentIdentity
registration.returnsstring[]contentApiKey, defaultClassroomId, contentApiBaseUrl
account.endpointstringGET /v1/platform/agent/account
servicesstring[]course, module, test, slide, video, game, evaluation
capabilitiesobjectContent API endpoint (with {classroomId}), unit, and USD price per service
addOnsobjectvideo_narration and video_render, with their Content API routes and prices
trial.creditsUsdnumber1
trial.creditsnumber50
trial.centsPerCreditnumber2
trial.emailVerificationRequiredForTrialbooleanfalse
trial.emailVerificationRequiredForBillingbooleantrue
pricing.endpointstring/v1/platform/pricing-catalog
paymentMethods.topUp.endpointstringPOST /v1/platform/agent/billing/session
paymentMethods.topUp.minimumUsdnumber5
paymentMethods.topUp.maximumUsdnumber5000
paymentMethods.usdcobjectdepositAddress and payment, the two x402 routes
shareRedirects.endpointstringGET /v1/platform/share-redirects/{shareToken}: public; answers { resourceType, url }, where an old Platform share link now lives. Limited to 60 requests per minute per IP
docsstringhttps://tutorflow.io/agent-platform/docs
healthstring/v1/platform/health
x402.enabledbooleantrue on production: agents can buy credits with USDC on Base
x402.networkstringeip155:8453 (Base)
x402.assetstringUSDC

The manifest and GET /v1/platform/pricing-catalog quote prices from the same source, and both price the Content API calls. In capabilities, creating a video is free (unitPriceUsd 0); its narration and render are the video_narration ($0.02 per scene) and video_render ($0.10 per render) add-ons. capabilities.slide carries imagePagePriceUsd (0.06) next to its text page price. See Pricing and Billing for every price.

POST /v1/platform/agent/register

Create an agent account and return its key. One transaction creates the account, the TutorFlow organization behind it, a default classroom, and 50 trial credits; then the tf_content_ key is issued.

Authentication

None. Register is limited to 5 attempts per client IP per rolling hour, and failed attempts such as a 422 count too. Register once and keep the key. Every answer carries X-RateLimit-* headers; over the limit the answer is 429 platform_rate_limit_exceeded with a Retry-After header and error.retryAfterSeconds.

Request Body

FieldTypeRequiredDescription
namestringYesWorkspace display name
slugstringYesUnique slug. Lowercase letters, numbers, and hyphens only. Cannot start or end with a hyphen
emailstringNoContact email. Trial works without it, but paid top-up cannot complete through public agent endpoints without an email
agentIdentitystringNoFree-form agent identity stored in the account metadata

Example Request

bash
curl -X POST https://api.tutorflow.io/v1/platform/agent/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My AI Tutor",
    "slug": "my-ai-tutor",
    "email": "agent@example.com",
    "agentIdentity": "claude via mcp"
  }'

Response

JSON
{
  "organizationId": "0f7a5db7-9f9d-4a46-9f5b-5b8f8ad73001",
  "apiKey": null,
  "keyPrefix": null,
  "contentApiKey": "tf_content_...",
  "contentApiKeyPrefix": "tf_content_a1b2c3d4",
  "defaultClassroomId": "00000000-0000-4000-8000-000000000010",
  "contentApiBaseUrl": "https://api.tutorflow.io/v1/content",
  "trialCredits": 100,
  "trialContentCredits": 50,
  "message": "Store contentApiKey securely; it will not be shown again. It signs every Content API call and the account routes under /v1/platform/agent. apiKey is null: legacy tf_platform_ keys are no longer issued.",
  "requiresEmailVerification": true,
  "verifyEmailResendEndpoint": "POST /v1/platform/agent/verify-email/resend",
  "nextSteps": {
    "createCourse": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/courses",
    "listCourses": "GET /v1/content/classrooms/00000000-0000-4000-8000-000000000010/courses",
    "createSlide": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/slides",
    "createVideo": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/videos",
    "accountInfo": "GET /v1/platform/agent/account"
  }
}
FieldDescription
organizationIdThe workspace id. Not the TutorFlow organization id. No route needs it
contentApiKeyThe live tf_content_ key. Use it for everything
defaultClassroomIdThe classroom every Content API call uses: /v1/content/classrooms/{defaultClassroomId}/...
contentApiBaseUrlThe Content API base URL for this environment
trialContentCreditsTrial credits on the account: 50, worth $1.00 at 2 cents per credit
trialCreditsThe same trial grant in cents: 100
apiKey, keyPrefixAlways null. Register no longer issues legacy tf_platform_ keys; a legacy key an older account already holds still works on the account routes
nextStepsContent API routes to try first. createCourse creates an empty course; to have TutorFlow write one, use POST .../courses/generate

Store contentApiKey immediately. It is returned only once.

Errors

HTTPerror.codeCauseAgent action
422platform_invalid_requestInvalid slug, invalid email, or malformed bodyFix the request
409platform_conflictSlug or email is already registeredChoose a new slug, or reuse the account that has the email
429platform_rate_limit_exceededMore than 5 register attempts from this IP in the past hourWait Retry-After seconds, or reuse an existing account

GET /v1/platform/agent/account

Return the account, its balance, its classrooms, and its rate limit.

Authentication

Live tf_content_ key.

bash
curl https://api.tutorflow.io/v1/platform/agent/account \
  -H "Authorization: Bearer tf_content_..."

Response

JSON
{
  "organizationId": "0f7a5db7-9f9d-4a46-9f5b-5b8f8ad73001",
  "name": "My AI Tutor",
  "slug": "my-ai-tutor",
  "status": "ACTIVE",
  "creditBalance": 88,
  "credits": 44,
  "overdraftCredits": 0,
  "overdraftLimit": 100,
  "isMetered": false,
  "rateLimitPerMinute": 1000,
  "isEmailVerified": false,
  "restrictedActions": ["add_payment_method", "usdc_payment", "create_billing_session"],
  "createdAt": "2026-10-16T09:00:00.000Z",
  "defaultClassroomId": "00000000-0000-4000-8000-000000000010",
  "sandboxClassroomId": null,
  "contentApiBaseUrl": "https://api.tutorflow.io/v1/content",
  "contentApiUrls": {
    "listClassrooms": "GET /v1/content/classrooms",
    "generateCourse": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/courses/generate",
    "listCourses": "GET /v1/content/classrooms/00000000-0000-4000-8000-000000000010/courses",
    "generateSlides": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/slides/generate",
    "generateTest": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/tests/generate",
    "createVideo": "POST /v1/content/classrooms/00000000-0000-4000-8000-000000000010/videos"
  }
}
FieldDescription
creditsThe prepaid balance, in classroom credits
creditBalanceThe same balance in US cents (credits x 2), not dollars: 44 credits answer 88
overdraftCreditsCredits drawn beyond the balance, waiting to be billed. Only on a metered account
overdraftLimitHow far the account may overdraw once metered
isMeteredtrue once a verified payment method lets the account overdraw
rateLimitPerMinuteThe account-route rate limit. Content API keys have their own limits
organizationIdThe workspace id
defaultClassroomIdThe classroom for Content API calls with a live key. null only for a workspace with no classroom yet
sandboxClassroomIdThe only classroom a test key reaches. null until the account has a test key
contentApiBaseUrlThe Content API base URL
contentApiUrlsThe first Content API calls for the default classroom, or null

restrictedActions lists action keys the agent cannot perform on this account yet. While email verification is pending, billing-related actions are gated. The array is empty once isEmailVerified is true.

GET /v1/platform/agent/billing/status

Return the balance and card status. It works even when the balance is empty.

Authentication

Live tf_content_ key.

bash
curl https://api.tutorflow.io/v1/platform/agent/billing/status \
  -H "Authorization: Bearer tf_content_..."

Response

JSON
{
  "hasPaymentMethod": true,
  "cardBrand": "visa",
  "cardLast4": "4242",
  "creditBalance": 5000,
  "credits": 2500,
  "overdraftCredits": 0,
  "overdraftLimit": 1000,
  "isMetered": true,
  "billingMode": "internal_invoice",
  "isEmailVerified": true,
  "restrictedActions": []
}

cardBrand and cardLast4 are null when no Stripe card is attached. restrictedActions is non-empty only while isEmailVerified is false: adding a payment method, USDC payments, and creating a billing checkout session all require a verified email. The balance fields mean the same as on GET /v1/platform/agent/account.

POST /v1/platform/agent/verify-email/resend

Generate a new 24-hour verification token and send the verification email to the account email (the email given at registration).

Authentication

Live tf_content_ key.

bash
curl -X POST https://api.tutorflow.io/v1/platform/agent/verify-email/resend \
  -H "Authorization: Bearer tf_content_..."

Response

JSON
{
  "message": "Verification email sent"
}

Errors

HTTPerror.codeCauseAgent action
200Email is already verifiedThe person who owns the email is added as an admin of the account, and gets a set-password email if they have no password. Continue to billing.
400platform_invalid_requestEmail is already verified, but no TutorFlow user can be attached to itContact support
400platform_invalid_requestNo email address on fileRegister a new account with email. No public route adds an email later

GET /v1/platform/agent/verify-email

Verify the account email from the link in the verification email.

Authentication

None.

Query Parameters

FieldTypeRequiredDescription
tokenstringYesEmail verification token from the verification email

Response

This endpoint redirects to the frontend instead of returning JSON:

ResultRedirect
Verification succeeded/sign-in/platform?verified=true
Token invalid or expired/sign-in/platform?verified=false

Agents normally do not call this endpoint directly. A human recipient clicks the email link, then the agent retries billing. Verifying also makes that person an admin of the account's organization, so they can sign in to TutorFlow and open the editorUrl of the account's content.

POST /v1/platform/agent/billing/session

Mint a one-hour TutorFlow checkout token and return a hosted checkout page URL. The agent forwards this URL to a human operator, who chooses a top-up amount and pays through Stripe.

Authentication

Live tf_content_ key. A verified email is required.

bash
curl -X POST https://api.tutorflow.io/v1/platform/agent/billing/session \
  -H "Authorization: Bearer tf_content_..."

Response

JSON
{
  "url": "https://tutorflow.io/agent-platform/checkout?token=6c7b1e8d-0b5a-4cb8-a1e4-0473f0001001",
  "sessionId": "6c7b1e8d-0b5a-4cb8-a1e4-0473f0001001"
}

sessionId is the TutorFlow checkout token. It is not a Stripe cs_... session ID.

Errors

HTTPerror.codeCauseAgent action
403email_verification_requiredThe account email is not verifiedCall POST /v1/platform/agent/verify-email/resend, have the recipient click the link, then retry

POST /v1/platform/agent/billing/x402/deposit

Create a USDC deposit address on Base for an amount. Available where x402 is enabled.

Authentication

Live tf_content_ key. A verified email is required.

Request Body

FieldTypeRequiredDescription
amountUsdnumberYesThe USD amount to deposit, from 5 to 5000. Credited at 2 cents per credit

Response

JSON
{
  "scheme": "exact",
  "network": "eip155:8453",
  "payTo": "0x...",
  "amountUsd": 10,
  "expiresInSeconds": 300,
  "paymentUrl": "POST /v1/platform/agent/billing/x402"
}

Pay exactly amountUsd in USDC to payTo, then call paymentUrl.

POST /v1/platform/agent/billing/x402

Verify a paid USDC deposit and credit the account at 2 cents per credit.

Authentication

Live tf_content_ key. A verified email is required.

Headers

HeaderRequiredDescription
x-paymentYesThe x402 payment payload for a deposit address from POST /v1/platform/agent/billing/x402/deposit

Response

JSON
{
  "isCredited": true,
  "credits": 550,
  "creditBalance": 1100
}

Errors

HTTPerror.codeCauseAgent action
402x402_payment_failedThe payment has not settled yetRetry after a few seconds
403agent_channel_onlyThe key belongs to a workspace that is not an agent accountBuy credits in TutorFlow Billing
403email_verification_requiredThe email is not verifiedVerify, then retry

GET /v1/platform/agent/checkout

Validate a checkout token for the hosted checkout page and return the account's display name and email. The checkout page calls it; an agent does not.

Authentication

None.

Query Parameters

FieldTypeRequiredDescription
tokenstringYesTutorFlow checkout token from POST /v1/platform/agent/billing/session

Response

JSON
{
  "email": "agent@example.com",
  "name": "My AI Tutor"
}

Errors

HTTPerror.codeCause
400platform_invalid_requestCheckout token has expired
404platform_not_foundToken is unknown or already used

POST /v1/platform/agent/checkout

Create the actual Stripe Checkout session from a valid TutorFlow checkout token and selected top-up amount.

Authentication

None. This endpoint is intended for the hosted checkout page.

Request Body

FieldTypeRequiredDescription
tokenstringYesTutorFlow checkout token from POST /v1/platform/agent/billing/session
amountUsdnumberYesTop-up amount. Minimum 5, maximum 5000

Response

JSON
{
  "url": "https://checkout.stripe.com/c/pay/cs_test_..."
}

The checkout token is invalidated after a Stripe URL is created.

Errors

HTTPerror.codeCauseAgent action
400platform_invalid_requestAmount is outside $5 to $5,000, or token is expiredChoose a valid amount or request a new billing session
403email_verification_requiredEmail became unverified before checkout completionVerify email, then request a new billing session
404platform_not_foundToken is unknown or already usedRequest a new billing session

GET /v1/platform/health

Validate that a key can authenticate against the Agent Platform.

Authentication

Live tf_content_ key, or a legacy tf_platform_ key. It works even when the balance is empty. Without a valid key it answers 401:

JSON
{
  "error": {
    "code": "platform_invalid_api_key",
    "message": "Invalid or missing Platform API key",
    "error": "Unauthorized",
    "statusCode": 401,
    "status": 401
  }
}

Response

JSON
{
  "status": "ok",
  "organizationId": "0f7a5db7-9f9d-4a46-9f5b-5b8f8ad73001"
}