Api Connection Guide

API Connection Guide (AI Interview)

This guide explains how to connect the mobile app to the Interview API so the AI Interview feature uses the live backend instead of mock/offline behavior.


Recent change: default production API

The mobile app now defaults to the production API when EXPO_PUBLIC_API_URL is not set:

  • Default (no env): https://api.civixapp.us — the app connects to the deployed API so AI Interview works out of the box.
  • Override: Set EXPO_PUBLIC_API_URL in apps/mobile/.env or EAS to use a different URL (e.g. http://localhost:3000 for local dev). See apps/mobile/lib/api-client.ts.

You do not need to set any env for production; only set it when pointing to a local or alternate API.


1. Setup

1.1 Get the API URL

  • Production: The app uses https://api.civixapp.us by default (no env required).
  • Local dev: Set EXPO_PUBLIC_API_URL=http://localhost:3000 (or your tunnel URL) in apps/mobile/.env.

1.2 Set the URL in the app (optional)

  • Local development: In apps/mobile, copy .env.example to .env and set EXPO_PUBLIC_API_URL=http://localhost:3000 (or your tunnel URL) only if you want to use a local API. Restart the dev server after changing.

  • EAS / production builds: No need to set EXPO_PUBLIC_API_URL unless you use a different API host. To override: eas secret:create --name EXPO_PUBLIC_API_URL --value "https://api.civixapp.us" --scope project.

1.3 Verify connection

  • Open the app (dev or installed build).
  • Go to Interview tab. The header should show:
    • "AI Assisted" when the API is reachable.
    • "Local Fallback" when the API is not set or unreachable.
  • If the API is unavailable, a non-blocking banner appears: "AI Interview unavailable. Try Mock Interview instead."

2. Testing

2.1 Manual test steps

  1. (Optional) Set EXPO_PUBLIC_API_URL in .env if using a non-default API; then run the app.
  2. Open Interview → accept disclaimer (if shown) → choose the featured Mock module (AI-Assisted when API is on).
  3. Start: You should see a welcome message from the AI officer.
  4. Progress phases: Chat through conversational phases, then answer reading/writing/civics prompts (the app calls evaluate during test phases).
  5. Complete: Finish the interview; the results screen should show and stats should save.

2.2 Expected behavior

  • With API URL set and server up: AI Interview starts, phase-locked flow advances correctly, and completion returns results.
  • With API URL empty or server down: Interview tab shows "Local Fallback"; starting the featured interview uses Mock Interview or shows an error with "Try Mock Interview."

2.3 Error scenarios

  • Network error: User sees a message like "Unable to connect. Please check your internet connection." and can retry or dismiss.
  • Rate limit (429): Message indicates rate limit and may show retry-after countdown; send is disabled until the countdown ends.
  • Session expired (404): Message indicates session not found/expired; user can go back and start a new interview.

2.4 Test the API on your own (local)

Use this to debug issues (e.g. billing, OpenAI quota) or develop without touching production.

1. Run the web API locally

cd apps/web
cp .env.example .env.local   # optional: add OPENAI_API_KEY for real AI
npm run dev

The API runs at http://localhost:3000. You’ll see all request and error logs in this terminal (including any OpenAI/billing errors).

  • Without OPENAI_API_KEY: Start and chat still work; the server uses fallback responses (no AI). Good for testing start/flow without billing.
  • With OPENAI_API_KEY: Full AI chat. If you hit quota or billing issues, the error will appear in this terminal and the app may show "Internal server error."

2. Point the mobile app at your local API

In apps/mobile, create or edit .env:

EXPO_PUBLIC_API_URL=http://localhost:3000

Restart the Expo dev server after changing .env.

  • iOS Simulator: localhost works.
  • Android emulator: Use http://10.0.2.2:3000 (emulator’s alias for host).
  • Physical device: Use your computer’s LAN IP (e.g. http://192.168.1.5:3000) or a tunnel like ngrok (ngrok http 3000 → put the HTTPS URL in EXPO_PUBLIC_API_URL).

3. Verify

  • Open the app → Interview tab. It should show "AI Assisted" when the local server is running.
  • Start an AI interview. Watch the terminal where npm run dev is running for any errors (e.g. Interview chat error: ... or OpenAI messages about quota/billing).

4. If you suspect billing/OpenAI

  • Check the web API terminal when you send a chat message; the thrown error (e.g. 429, 402, invalid key) will be logged there.
  • Without an API key, chat uses fallback text and does not call OpenAI, so you can confirm the rest of the flow works.

2.5 Test the deployed Interview API

To verify the live server (e.g. https://api.civixapp.us) without the mobile app, run the interview flow from the repo:

cd apps/web
npm run test:interview-api

This script:

  1. Calls GET /api/health and prints status and OpenAI config.
  2. Calls POST /api/interview/start with { mode: 'standard', language: 'en' }.
  3. Calls POST /api/interview/chat through conversational phases.
  4. Calls POST /api/interview/evaluate for reading/writing when those phases are active.
  5. Calls POST /api/interview/evaluate for civics using the server-provided activeCivicsQuestion (questionId + answerToken).
  6. Calls POST /api/interview/complete and verifies response shape.

If any step returns 500, the script prints the error and optional detail (when the server sends it). Use a different base URL with:

API_URL=https://your-deployed-api.vercel.app npm run test:interview-api

You can also validate exemption flow policy by changing mode:

INTERVIEW_MODE=55_15 API_URL=https://your-deployed-api.vercel.app npm run test:interview-api

To validate a deployment that has signed-request enforcement enabled, provide the same signing secret used by the API:

INTERVIEW_SIGNING_SECRET=your-secret API_URL=https://your-deployed-api.vercel.app npm run test:interview-api

2.6 Signed-request security mode notes

Interview API supports INTERVIEW_SECURITY_MODE=off|report|enforce.

  • off: no signature checks.
  • report: detects missing/invalid signatures but does not block requests.
  • enforce: blocks unsigned, stale, replayed, or invalidly signed requests.

When signatures are enabled:

  • Include X-Request-Signature-Version.
  • Current enforced version is v2, which signs METHOD + PATH + TIMESTAMP + NONCE + DEVICE_ID + SHA256(BODY).

Important constraint for public mobile apps:

  • Do not put INTERVIEW_SIGNING_SECRET into a mobile build. It is extractable from client apps.
  • For mobile clients, use off or report unless you introduce a trusted server-side signer/proxy or another secure auth layer.

3. Troubleshooting

IssueWhat to check
API not connectingIf you override the URL: confirm EXPO_PUBLIC_API_URL has no trailing slash. For device/emulator, use your machine’s IP or a tunnel, not localhost. Check firewall and that the API server is running. With no env set, the app uses production (https://api.civixapp.us).
Interview not startingEnsure API is reachable (see Verify connection above). Check that the start endpoint returns 200 and a valid session.
Internal server error (500)API is reachable but start/chat/complete returns 500. Check server logs (e.g. Vercel dashboard, or the terminal running npm run dev if testing locally). Common causes: OpenAI billing/quota or invalid key (chat calls), invalid request body, session/Redis failure, or missing env. Use Test the API on your own to see errors in your terminal.
OpenAI billing / rate limitIf the chat or evaluate response has code: 'OPENAI_BILLING' or OPENAI_RATE_LIMIT, add payment or credits to your OpenAI account, or wait and retry for rate limits. For invalid key, the response will have code: 'OPENAI_AUTH'.
Budget cap reachedIf OpenAI budget guardrails are configured (OPENAI_DAILY_REQUEST_CAP, OPENAI_BUDGET_MONTHLY_USD), AI responses can fall back to non-AI scripted logic when caps are exceeded.
Messages not sendingCheck network; if 429, wait for retry-after. If 409 OUT_OF_ORDER, call the endpoint that matches the active phase (chat for conversation, evaluate for test phases) and use the latest civics answerToken.
401 MISSING_SIGNATURE_HEADERS / INVALID_SIGNATUREAPI is in security enforce mode but the client is unsigned. Use off or report for public mobile clients, or route through a trusted signer/proxy.
Still "Local Fallback" with URL setRestart the app after changing env. For EAS builds, confirm the secret is set and the build was made after that.
Default production URLWith no env set, the app uses https://api.civixapp.us. If that domain is not yet deployed, set EXPO_PUBLIC_API_URL to your live API base URL or leave unset and deploy the web app first.

3.1 Fallback behavior

  • If the API is unavailable, the app does not block: users can still use Mock Interview and all other offline features.
  • Session is saved locally during an AI interview and cleared when the interview is completed or when starting a new one.

4. Related docs