ShipAgent

AI

Connect one AI provider, prove it answers through the app's own code, and set up callbacks for video.

The site can generate text, images or video through your provider when this check passes with the app running:

pnpm tsx scripts/check-ai.ts --url http://localhost:3000

Its last line must be AI: all checks passed. If your product makes images, the agent also runs it once with --image, which generates one small image and costs a few cents.

Supported providers

The list lives in config/ai-providers.ts. A provider is on when its key is set. Every key stays on the server.

Provider Env variable(s) Can do
OpenRouter OPENROUTER_API_KEY chat (one key, many vendors' models)
OpenAI OPENAI_API_KEY chat, image
Anthropic ANTHROPIC_API_KEY chat
Google GOOGLE_GENERATIVE_AI_API_KEY chat, image
DeepSeek DEEPSEEK_API_KEY chat
xAI XAI_API_KEY chat, image
fal.ai FAL_KEY image, video
Replicate REPLICATE_API_TOKEN image, video
KIE KIE_API_KEY image, video (image references also need Storage)
Any OpenAI-compatible API CUSTOM_OPENAI_API_KEY, CUSTOM_OPENAI_BASE_URL, NEXT_PUBLIC_CUSTOM_OPENAI_MODELS (id:Name,id2:Name2) chat

fal reads one variable, FAL_KEY. There is no separate fal secret.

Start with one provider.

  • Text only: OpenRouter.
  • Images or video: fal or Replicate. Both do image and video.
  • Add a second provider only when a model you want is not on the first.

You

Value Where you get it
Which provider tell the agent what your product generates; it suggests one from the list above
OPENROUTER_API_KEY openrouter.ai → sign in → Keys → Create key
REPLICATE_API_TOKEN replicate.com/account/api-tokens
FAL_KEY fal.ai → sign in → Dashboard → Keys → Add key
DEEPSEEK_API_KEY platform.deepseek.com/api_keys
OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, XAI_API_KEY, KIE_API_KEY the provider's console → API keys
Billing on the provider account the provider's billing page. The API cannot add a card, so only you can
A tunnel login (video, local only) only if the tunnel tool the agent starts asks you to log in

These click paths could not be checked against anything in the repository (check the dashboard, menus move). If a page has moved, look for "API keys" in the provider's dashboard.

Agent

  • Writes your key into .env.local and runs the check. For each chat provider, it makes one tiny chat call through the same code the app uses.
  • Adjusts the model list in config/ai-models.ts to the models you want to offer. The demos use the first entry of each list.
  • For images: generates one small image with the cheapest-looking model, or one you name.
  • For video: sets WEBHOOK_BASE_URL, the public address the provider calls back when a video is ready. Locally that is a tunnel to localhost:3000, because a provider cannot reach your machine directly. Video jobs are also kept in Redis, so it needs UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN.
  • Turns on callback signature checks: Replicate's secret is read from your account and written for you; for fal it sets FAL_VERIFY_WEBHOOKS=true (nothing to paste).
  • Confirms that every route that calls a paid provider either charges credits (and refunds them on failure) or is admin-only.
  • Calls the image demo signed out and expects the sign-in wall, which proves the route is live.
  • Asks you to try the demos as admin: /ai-demo/chat (pick a model whose provider has a key), and /ai-demo/image or /ai-demo/video if you set those up.

The demo pages are admin-only and charge no credits; they exist to test your keys. A paid feature for your users is built with the add-tool-page skill (see Add a tool page).

What a pass looks like

✓ model registry: <n> chat, <n> image, <n> video models, every provider exists in config/ai-providers.ts
✓ provider keys set: OpenRouter
✓ openrouter: <model id> answered "ok" (<n> in / <n> out tokens)
  <route>: admin-only, charges no credits (demo)
✓ http://localhost:3000/api/ai-demo/image → 401 "Please sign in to use the AI demo." (signed-out wall works, the route is wired)

AI: all checks passed

With --image, one more line reports the image: ✓ <provider>: <model> returned image/…, <size> KB … in <seconds>s. With a video provider, two more: ✓ <provider>: <url> reaches the app and ✓ video task store: Redis configured.

When it fails

The check or the app says What it means Fix
no AI provider key is set nothing pasted yet pick a provider and paste its key
<provider>: <model> failed: … not found / does not exist the provider retired or renamed that model the agent updates config/ai-models.ts
openrouter: … failed: Missing Authentication header an invalid OpenRouter key paste a fresh key
… 401 / 403 / invalid api key wrong key, or no billing on the account new key; add billing
… 429 / quota / insufficient out of provider credit, or rate-limited top up; try again later
replicate: REPLICATE_API_TOKEN rejected wrong token copy it again from the api-tokens page
WEBHOOK_BASE_URL is not set or is local video has no address to call back the agent sets it (a tunnel locally, your domain in production)
<url> answered 404 / 502 / 530 or unreachable the tunnel is down or points elsewhere, or the app is not running the agent restarts the tunnel and the app and updates the address
video task store has no Redis Upstash not set set UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN
… calls a paid provider for any signed-in user without consumeCredits a route generates for free the agent adds the credit charge, or makes it admin-only again
--url → 307 / 308 the address redirects (http to https, bare domain to www) use the final address
in the app: Missing API key: X is not configured for Y the running app does not see the key restart the app, or redeploy with the key in the production env

Going live

  • The same keys go into the production env, and WEBHOOK_BASE_URL becomes your real domain.
  • NEXT_PUBLIC_CUSTOM_OPENAI_MODELS is read at build time: change it, then rebuild.
  • The agent reruns the check against your domain with the production env.
  • You: set spending limits and billing alerts in each provider's dashboard. The API cannot set them.