# Infrai > One API, one key, one wallet, one bill across every online-infrastructure > capability an AI agent needs — AI calls plus the infra an agent uses on a user's > behalf, all under a single account. Zero install: it is a plain HTTPS REST API > (callable from any language, or via the MCP server). Built AI-first: every > response carries cost/vendor/latency/cache metadata and every error carries a > machine-readable `hint` + `retryable` flag. ## AI inference is OpenAI-compatible — just point the OpenAI SDK at Infrai You already know the OpenAI API. To run AI inference through Infrai, change ONLY the base URL and key — no new SDK, no new shapes: ```python from openai import OpenAI client = OpenAI(base_url="https://api.infrai.cc/v1", api_key=os.environ["INFRAI_API_KEY"]) resp = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "Summarize the latest news"}]) print(resp.choices[0].message.content) ``` - Works for: `chat/completions`, `embeddings`, `images/generations`, `audio/speech` (TTS), `audio/transcriptions` (ASR), and `models` / `models/{id}` — the standard OpenAI endpoints. - Smart cross-vendor routing survives the OpenAI `model` field: `model:"auto"` lets Infrai pick the best/cheapest live vendor (pass `task`/`prefer` via `extra_body`); `model:"gpt-4o-mini"` / `"deepseek-chat"` / `"vendor/model"` pins it. - Infrai's differentiation is carried WITHOUT breaking compat: cost/vendor/cache/region in an extra top-level `infrai` object + `X-Infrai-*` response headers (an OpenAI client ignores the extra field). - The old custom `/v1/ai/chat`, `/v1/ai/embed`, `/v1/ai/image`, `/v1/ai/tts`, `/v1/ai/asr`, `/v1/ai/vision`, `/v1/ai/moderation`, `/v1/ai/models` shapes are RETIRED — use the OpenAI surface above. ```bash curl -X POST https://api.infrai.cc/v1/chat/completions \ -H "Authorization: Bearer $INFRAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "auto", "messages": [{"role": "user", "content": "Summarize the latest news"}]}' ``` ## How to call Infrai's infra capabilities (REST, zero install) Everything NON-AI (email / sms / pdf / image-process / storage / scheduling / realtime / video / …) is a plain Infrai REST call: - Base URL: `https://api.infrai.cc` (China SKU: `https://api.cn.infrai.cc`). Paths already include `/v1`. - Auth: `Authorization: Bearer $INFRAI_API_KEY` (project key). `Content-Type: application/json`. - Response envelope: `{ ok, data, error, metadata }`. Read `ok`; on failure read `error.code` + `error.hint`. - Idempotency: callers may optionally send an `Idempotency-Key` header for advanced replay/conflict control; ordinary writes do not require it. ```bash curl -X POST https://api.infrai.cc/v1/email/send \ -H "Authorization: Bearer $INFRAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"to": "a@b.com", "subject": "Hi", "body": "Hello from Infrai"}' ``` Onboarding: the developer gets a project key by registering at the infrai console — sign in with Google or GitHub for $2 free credit (email sign-in works too, starting at $0). Use that key as `INFRAI_API_KEY`. There is NO anonymous/unregistered key. Never ask the user to type email/OTP/card/KYC into an API request — on `402 INSUFFICIENT_CREDIT`, call `POST /v1/account/topup` and open the returned `checkout_url` in a browser. ## 19 GA business modules + account-billing control plane (295 REST routes) Generated from the discovery manifest — real method+path per module. The headline paths are the first few; the full per-capability table (method/path/params/errors/ examples) is in the discovery manifest and the docs site. - AI runtime (`/v1/ai`, 13 routes) — rerank / token-count / cost-estimate / batch / upscale — chat/embed/image/tts/asr are OpenAI-compatible (see top) `/v1/ai/batch/cancel/{id}` · `/v1/ai/batch/export/{id}` · `/v1/ai/batch/list` - AI video (`/v1/video`, 7 routes) — text/image-to-video generation + status `/v1/video/cancel/{id}` · `/v1/video/capabilities` · `/v1/video/delete/{id}` - Email (`/v1/email`, 18 routes) — transactional-first send, domains, suppression `/v1/email/batch/send` · `/v1/email/domain/delete/{domain}` · `/v1/email/domain/get/{domain}` - SMS (`/v1/sms`, 23 routes) — send / OTP / verify / templates / signatures `/v1/sms/batch/send` · `/v1/sms/cancel/{id}` · `/v1/sms/events/{id}` - Scheduling (`/v1/cron` · `/v1/queue`, 25 routes) — cron / queues / delayed jobs `/v1/cron/create` · `/v1/queue/ack` · `/v1/cron/delete/{id}` - Observability (`/v1/errors` · `/v1/flags` · `/v1/logs` · `/v1/metrics`, 23 routes) — error capture / metrics / logs / feature flags `/v1/errors/capture` · `/v1/flags/delete/{key}` · `/v1/logs/ingest` · `/v1/metrics/batch` - Analytics (`/v1/analytics`, 8 routes) — event track / identify / funnels / cohorts `/v1/analytics/alias` · `/v1/analytics/batch` · `/v1/analytics/group` - Captcha (`/v1/captcha`, 1 routes) — verify / widget `/v1/captcha/verify` - PDF (`/v1/pdf`, 19 routes) — generate / merge / split / OCR / watermark `/v1/pdf/compress` · `/v1/pdf/convert` · `/v1/pdf/decrypt` - Image processing (`/v1/image`, 21 routes) — resize / transform / metadata `/v1/image/background_remove` · `/v1/image/batch/cancel/{id}` · `/v1/image/batch/status/{id}` - Realtime (`/v1/realtime`, 11 routes) — pub/sub channels + presence `/v1/realtime/channel/create` · `/v1/realtime/channel/delete/{channel}` · `/v1/realtime/channel/get/{channel}` - Object storage (`/v1/storage`, 22 routes) — buckets / presigned URLs / lifecycle `/v1/storage/bucket/create` · `/v1/storage/bucket/delete/{bucket}` · `/v1/storage/bucket/get/{bucket}` - Auth & identity (`/v1/auth`, 34 routes) — end-user auth / sessions / JWT / OAuth-as-a-service `/v1/auth/consent/check/{user_id}/{category}` · `/v1/auth/consent/grant/{user_id}` · `/v1/auth/consent/list_for_user/{user_id}` - Realtime comms (RTC) (`/v1/rtc`, 7 routes) — rooms / tokens / participants for audio-video `/v1/rtc/participant/kick/{room}` · `/v1/rtc/participant/list/{room}` · `/v1/rtc/room/create` - Managed database (`/v1/db`, 12 routes) — provision / query / branches / backups `/v1/db/branch/create` · `/v1/db/branch/delete/{project_id}` · `/v1/db/branch/get/{project_id}` - DNS (`/v1/dns`, 10 routes) — zones / records / verification `/v1/dns/domain/add` · `/v1/dns/domain/delete` · `/v1/dns/domain/get` - Risk & fraud (`/v1/risk`, 3 routes) — risk scoring / device + IP signals / rules `/v1/risk/device/fingerprint` · `/v1/risk/event/report` · `/v1/risk/score` - Vector store (RAG) (`/v1/vector`, 7 routes) — collections / upsert / ANN query — self-hosted pgvector default; pair ai.embed → vector.upsert/query `/v1/vector/collection/create` · `/v1/vector/collection/delete` · `/v1/vector/collection/get` - Web knowledge (`/v1/web`, 2 routes) — search the live web + read a single page (scrape) for LLM grounding `/v1/web/scrape` · `/v1/web/search` - Account & billing (`/v1/account`, 29 routes) — balance / usage / top-up / keys `/v1/account/autorecharge/configure` · `/v1/account/autorecharge/get` · `/v1/account/balance` ## Flows & decision guides (multi-step recipes per module) A flow is an ordered sequence of REST capabilities; each step in the discovery manifest carries the response fields it returns, so an AI can chain them (job_id → poll, checkout_url → open, message_id → track). Full step/response detail is in `/v1/discovery` (`flows` + `decision_guides`); below is the index. **Choosing a login mode** (default: `email_otp_login`): - Most consumer apps (mobile/web), lowest friction, fastest conversion → `email_otp_login`, `phone_otp_login`, `social_login` — Passwordless: no password to store/reset; verify == login in ONE call; new user auto-registers. Add social for one-tap. - Social-first / B2C web where users expect 'Sign in with Google/Apple' → `social_login`, `email_otp_login` — Unified-brand OAuth transit (infrai hosts the provider apps, NO BYOK); session returns to YOUR return_to. OTP as fallback. - Tools / dashboards / apps that specifically want a classic password → `password_signup`, `password_login`, `password_reset` — Email+password, but verify the email via OTP before the account is usable. Reset is code-style (no plaintext recovery). - China-market apps → `phone_otp_login`, `social_login` — Phone OTP is the dominant CN pattern; social via WeChat/Alipay (when configured). - Enterprise / B2B → `social_login` — OAuth now; SSO/SAML is Phase-5. Password optional via password_signup. **Flow recipes** (capability chain per flow): - **account-billing** - Onboard a new account (identity → key → fund) (`account_onboarding`): `account.whoami` → `account.keys.create` → `account.topup` - Top up the wallet (create session → pay → confirm credit) (`account_topup`): `account.topup` → `account.balance` - Register a webhook (register → test → handle signed events) (`account_webhook_setup`): `account.webhooks.register` → `account.webhooks.test` → `account.webhooks.deliveries` - **ai-runtime** - Pick a voice (per vendor) then synthesize speech (`tts_select_voice_then_speak`): `ai.tts.voices` → `POST /v1/audio/speech` - Batch AI requests (async submit → poll → page results) (`ai_batch_submit_poll_fetch`): `ai.batch.submit` → `ai.batch.status` → `ai.batch.results` - **ai-video** - Generate a video (async submit → poll → fetch URL) (`video_generate_poll_fetch`): `video.generate` → `video.status` → `video.get` - Check provider capabilities before generating (`video_select_then_generate`): `video.capabilities` → `video.generate` - **auth** - Email OTP login (passwordless · signup-on-first-login) (`email_otp_login`): `auth.email.send_code` → `auth.email.verify` - Phone (SMS) OTP login (passwordless) (`phone_otp_login`): `auth.phone.send_code` → `auth.phone.verify` - Third-party login (Google/GitHub/Apple/Meta/X) · unified-brand transit (`social_login`): `auth.oauth.authorize_url` - Password signup WITH OTP email verification (`password_signup`): `auth.user.create` → `auth.email.send_code` → `auth.email.verify` - Password login (email + password) (`password_login`): `auth.session.create` - Password reset (code-style · no plaintext recovery) (`password_reset`): `auth.password.reset_request` → `auth.password.reset_confirm` - Session lifecycle (refresh / logout / multi-device) (`session_lifecycle`): `auth.session.refresh` → `auth.session.list_for_user` → `auth.session.revoke` - Bind more login methods to one user (`bind_more_identities`): `auth.identity.add` - Verify a session JWT offline (zero round-trip) (`verify_session_offline`): `auth.token.jwks` - **captcha** - Protect a form with CAPTCHA (verify submissions) (`captcha_protect_form`): `captcha.verify` - **comm-email** - Send a transactional email and track delivery (`email_send_and_track`): `email.send` → `email.get` → `email.event.list` - Add and verify a sender domain before sending (`email_domain_onboarding`): `email.domain.verify` → `email.domain.get` - Create a template, preview it, then send by template_id (`email_template_send`): `email.template.create` → `email.template.preview` → `email.send` - **db** - Provision a managed database and branch it (`db_provision_and_branch`): `db.project.create` → `db.branch.create` - **dns** - Bind a custom domain and point it at your app (`dns_bind_domain_and_point`): `dns.domain.add` → `dns.record.create` → `dns.domain.verify` - **image-process** - Upload → transform → deliver an image (`image_upload_process_deliver`): `image.upload` → `image.process` → `image.get` - Batch image processing (async submit → poll → read items) (`image_batch_submit_poll`): `image.batch.submit` → `image.batch.status` - **observability** - Capture and triage an error (capture → inspect group → resolve) (`error_triage`): `errors.capture` → `errors.group_detail` → `errors.resolve` - **pdf** - Generate a PDF from HTML/Markdown/template (`pdf_generate_then_store`): `pdf.generate` - Parse a PDF for RAG ingestion (`pdf_parse_for_rag`): `pdf.parse` - Track an async PDF job (`pdf_async_job`): `pdf.job.get` - **realtime** - Presence channel — authorize client, publish, and read who's online (`realtime_presence_channel`): `realtime.channel.create` → `realtime.token.issue` → `realtime.publish` → `realtime.presence.get` - **rtc** - Create a live audio/video room and let a participant join (`rtc_join_room`): `rtc.room.create` → `rtc.token.issue` → `rtc.participant.list` - **scheduling** - Schedule a recurring job (create → inspect runs → pause) (`cron_lifecycle`): `cron.create` → `cron.runs.list` → `cron.pause` - Process a message queue (create → publish → consume → ack/DLQ) (`queue_worker`): `queue.create` → `queue.publish` → `queue.consume` → `queue.ack` - **sms** - Send an SMS and track delivery (`sms_send_and_track`): `sms.send` → `sms.status` → `sms.events` - Send and verify a one-time passcode (managed OTP) (`sms_otp_verify`): `sms.otp` → `sms.verify` - Register and verify a sender number before sending (`sms_sender_onboarding`): `sms.sender.register` → `sms.sender.get` - Register a signature + template for China delivery (`sms_china_signature_setup`): `sms.signature.create` → `sms.template.create` → `sms.send` - **storage** - Browser direct upload via presigned URL (`storage_browser_direct_upload`): `storage.object.presign` → `storage.object.head` - Server-side object lifecycle (`storage_object_lifecycle`): `storage.object.put` → `storage.object.list` → `storage.object.delete` - Large-file multipart upload (`storage_large_file_multipart`): `storage.multipart.create` → `storage.multipart.presign_part` → `storage.multipart.complete` - **vector** - Build a RAG index (create → upsert → query) (`vector_rag_ingest_and_search`): `vector.collection.create` → `vector.upsert` → `vector.query` **Per-module decision guides** (pick the right call/option): - **account-billing** — Funding, keys, and plan choices for an integration (default: `account_onboarding`) - Pay-as-you-go, occasional or spiky usage, no commitment → `account.topup`, `account.balance`, `account.budget.set` - Steady usage and you want it to never run dry / unattended workloads → `account.autorecharge.configure`, `account.payment_method.set_default` - You want bundled entitlements, higher rate limits, or team features → `account.tier.upgrade`, `account.subscription.get` - Multiple apps/environments under one account → `account.keys.create` - A key may be leaked → `account.keys.suspected_compromise`, `account.keys.rotate` - **ai-runtime** — Choosing the right AI-runtime call (default: `OpenAI-compatible /v1/chat/completions for chat`) - You need chat, embeddings, image generation, TTS, ASR, vision, or moderation → `/v1/chat/completions`, `/v1/embeddings`, `/v1/images/generations`, `/v1/audio/speech`, `/v1/audio/transcriptions` - Many requests, latency-tolerant, cost-sensitive → `ai_batch_submit_poll_fetch` - Re-rank retrieval candidates for RAG → `ai.rerank` - Know the price/token count before committing a call → `ai.cost.estimate`, `ai.cost.compare`, `ai.tokens.count` - **ai-video** — Generating video: vendor, resolution, sync model (default: `model=auto, resolution=720p, poll video.status`) - You don't care which vendor, just want good value → `model=auto` - You need 1080p/4k or a long duration → `video_select_then_generate` - You can't keep a polling loop open → `webhook_url on video.generate` - You'll fetch the video later or list past jobs → `store=true` - **analytics** — Choosing the right analytics query (default: `analytics.query.events`) - Inspect or debug what actually happened (raw event stream, filtered) → `analytics.query.events` - Measure whether users come back over time → `analytics.query.retention` - Understand the sequences users take after an action → `analytics.query.path` - Tie events to a known user and merge anonymous history → `analytics.identify`, `analytics.alias` - High-volume client-side instrumentation → `analytics.batch` - **captcha** — Verifying a CAPTCHA token server-side (default: `captcha_protect_form`) - Frictionless protection — most sign-up / login / contact forms → `score_threshold=0.5` - High-fraud UX needs stricter gating → `score_threshold=0.7`, `expected_hostname=` - **comm-email** — Choosing how to send email (inline, template, batch, domain) (default: `email_send_and_track`) - One-off transactional email with content built at call time → `email.send` - Reusable email (welcome, receipt, OTP) with per-send variables → `email_template_send` - Sending from your own domain (custom 'from' address) → `email_domain_onboarding` - Many personalized emails in one shot (<=100) → `email.batch.send` - Need to know if a message was delivered/opened/bounced → `email.get`, `email.event.list` - **db** — Choosing a database vendor and plan (default: `db_provision_and_branch`) - You need cheap copy-on-write branches for previews/CI → `db_provision_and_branch` - Data residency or latency matters → `db.project.create` - **dns** — Choosing a DNS record type (default: `A record, ttl=300, proxied=false`) - Point a hostname at an IPv4 / IPv6 address → `record_type=A`, `record_type=AAAA` - Point a hostname at another hostname (SaaS / app target) → `record_type=CNAME` - Email routing or domain verification → `record_type=MX`, `record_type=TXT` - **image-process** — Choosing an image operation (default: `image.process pipeline, store=false (inline)`) - Multiple edits on one image (resize + watermark + convert) → `image.process` - Fit content intelligently to an aspect ratio (thumbnails, social crops) → `image.smart_crop` - Shrink for web delivery → `image.compress`, `image.convert` - You'll re-fetch or deliver the asset later → `image_upload_process_deliver` - Read content from an image, not edit it → `image.ocr`, `image.tag`, `image.moderate`, `image.metadata` - **observability** — Errors, logs, metrics, and flags — which surface to use (default: `error_triage`) - An exception or failure happened and you want it grouped and triaged → `errors.capture`, `errors.message` - You need searchable, high-cardinality text events → `logs.ingest`, `logs.search` - You need numeric time-series (rates, latencies, counts) → `metrics.report`, `metrics.query` - You want to gate or gradually ship a feature → `flags.set`, `flags.rollout`, `flags.get_value` - **pdf** — Generating and reading PDFs (default: `pdf.generate from HTML, store=false`) - One-off document from markup → `pdf_generate_then_store` - The same layout rendered repeatedly with data (invoices, certificates) → `pdf.template.create`, `pdf.generate` - You'll email/download the PDF later → `store=true` - Ingest a PDF for RAG / search → `pdf_parse_for_rag` - **realtime** — Choosing a realtime channel type and token style (default: `realtime_presence_channel`) - Broadcast / public feed anyone may subscribe to → `type=public` - Auth-gated channel (per-user or per-room data) → `type=private`, `realtime.token.issue` - You need a live online roster (who's here / cursors) → `type=presence`, `realtime.presence.get` - Issuing a client connection token → `realtime.token.issue` - **risk** — Choosing a risk event_type and acting on the disposition (default: `event_type=login, act on disposition`) - Account-takeover / credential-stuffing defense → `event_type=login` - Payment / checkout fraud → `event_type=payment` - Signup abuse / fake accounts → `event_type=signup` - **rtc** — Choosing an RTC room shape and participant permissions (default: `rtc_join_room`) - Voice-only huddle / audio room → `rtc.room.create`, `rtc.token.issue` - Webinar / livestream (few speakers, many viewers) → `can_publish=true for hosts`, `can_publish=false can_subscribe=true for audience` - You want a persistent, queryable room object → `vendor=livekit` - **scheduling** — Cron vs one-shot vs queue (default: `cron_lifecycle`) - Run something on a repeating schedule (daily report, hourly sync) → `cron_lifecycle` - Run something exactly once at a future time (delayed task, reminder) → `cron.create` - Fan work out to workers with retries and at-least-once delivery → `queue_worker` - **sms** — Choosing the right SMS path (OTP vs raw send, region, sender) (default: `sms_send_and_track`) - Phone verification / 2FA codes → `sms_otp_verify` - Notifications, alerts, reminders (non-OTP) → `sms_send_and_track` - Sending to China → `sms_china_signature_setup` - Using a branded / dedicated sender number → `sms_sender_onboarding` - **storage** — Uploading and serving objects (presign vs put, public vs private) (default: `storage_browser_direct_upload`) - The browser/mobile client holds the bytes (file picker, drag-drop) → `storage_browser_direct_upload` - Your backend already has the bytes (generated/processed server-side) → `storage.object.put` - File is multi-GB or uploaded in chunks/resumable → `storage_large_file_multipart` - Asset is user-private or sensitive (invoices, PII, paid content) → `storage.object.presign` - Asset is a public static file (avatars, public images) → `storage.object.set_acl` - **vector** — Building a vector index (metric, dimension) (default: `vector_rag_ingest_and_search`) - Using OpenAI / most modern text embeddings (normalized vectors) → `metric=cosine` - Embeddings are raw inner-product / not normalized → `metric=dotproduct` - Embeddings live in a true geometric space and absolute distance matters → `metric=euclidean` ## SSOT contract surface - Enums, error codes, and request schemas are generated from a single source of truth (`infrai-spec/`), exposed in `discovery/manifest.json` and the live discovery endpoints. - Every error code has a `hint` (human-readable) and a `retryable` boolean. - Callers may optionally send an `Idempotency-Key` header for advanced replay/conflict control; ordinary writes do not require it. - MCP server (`npx -y @infrai/mcp-server`) is a thin wrapper over these same REST endpoints; same contract, same params, same envelope. ## Error codes — what they mean and what to do Every error response carries `error.code` (machine-readable), `error.hint` (human-readable), and `error.retryable` (boolean). The two codes most commonly confused are: | Code | HTTP | Retryable | Meaning | What to do | |---|---|---|---|---| | `CAPABILITY_NOT_IMPLEMENTED` | 501 | No | The capability has no live implementation/adapter yet — a **development gap**. Adding a vendor key will NOT make it work. | Do not retry. Check the discovery index for alternative capabilities. This is Infrai's honest refusal — the route exists but the backend is not built. | | `VENDOR_NOT_CONFIGURED` | 503 | No | The capability IS implemented, but no usable vendor key/credential is configured for this project — a **config gap**, not a transient outage. | Do not retry. Hydrate a vendor key via the console or `account.vendors.*`. Once a key is added, the same call works immediately. | > **Key distinction:** `501 CAPABILITY_NOT_IMPLEMENTED` = Infrai hasn't built it yet. > `503 VENDOR_NOT_CONFIGURED` = Infrai built it, but your project hasn't configured a vendor key. > If a documented endpoint returns `VENDOR_NOT_CONFIGURED`, the capability is live — you just need to add a key. Other common error codes: | Code | HTTP | Retryable | Meaning | |---|---|---|---| | `INVALID_ARGUMENT` | 400 | No | Missing or invalid request parameter (see `error.param`). | | `UNAUTHORIZED` | 401 | No | Invalid or missing project key (`Authorization: Bearer`). | | `INSUFFICIENT_CREDIT` | 402 | No | Wallet balance insufficient. Call `POST /v1/account/topup` and open the returned `checkout_url`. | | `RATE_LIMIT_USER` / `RATE_LIMIT_ACCOUNT` | 429 | Yes | Per-user or per-account rate limit. Back off and retry. | | `RATE_LIMIT_VENDOR` | 429 | Yes | Vendor rate limit; Infrai is failing over. Back off and retry. | | `VENDOR_DOWN` | 503 | Yes | Primary vendor unavailable; failover chain engaged. Retry with backoff. | | `VENDOR_TIMEOUT` | 504 | Yes | Upstream vendor timed out. Retry with backoff (max 3 attempts). | | `VENDOR_AUTH_ERROR` | 502 | Yes | Vendor key rejected. Infrai rotates keys; retry with backoff. | The full error-code registry (with `http`, `retryable`, `backoff`, and `human_hint` for every code) is in `infrai-spec/errors/registry.yaml` and surfaced via the discovery endpoints. ## Docs - [REST API](https://docs.infrai.cc/Infrai_REST_API.md): module-by-module REST surface (METHOD + path + curl/python/js) - [REST Conventions](https://docs.infrai.cc/Infrai_REST_Conventions.md): auth, canonical JSON, optional idempotency control, error envelope, retries - [Pricing One-Pager](https://docs.infrai.cc/Infrai_Pricing_OnePager.md): plans + tiers ## Discovery The API is self-describing — never guess a parameter name or an allowed value. Find the capability in the index, then read its detail schema before building a request: - [GET /v1/discovery](https://api.infrai.cc/v1/discovery) — capability INDEX: id / method / path / availability / current vendors / billing + per-capability curl/python/js examples. Lightweight: it carries NO params — use it to locate a capability, then call the detail endpoint for its inputs. - [GET /v1/discovery/{capability}](https://api.infrai.cc/v1/discovery/email.send) — single-capability DETAIL. `params` is the full request JSON Schema: `params.properties..enum` is the exact closed set of allowed values for an enum field (e.g. `account.webhooks.register` → the `events[]` enum; `sms.send` → `route_class` / `mode`), and nested array elements carry their own `items` schema (batch `messages`, lifecycle `rules`, multipart `parts`, …). `dynamic_params` lists runtime-injected choices — e.g. the `vendor` enum reflects which providers your account actually has keys for. `response` is the result shape, so you can chain calls. Always prefer these live values over memorised guesses. - [.well-known/ai-plugin.json](https://api.infrai.cc/.well-known/ai-plugin.json) - [.well-known/llms.json](https://api.infrai.cc/.well-known/llms.json) - [.well-known/pricing.json](https://api.infrai.cc/.well-known/pricing.json) API host: `https://api.infrai.cc` · 20 modules · 295 routed capabilities