Vercel · Opus 5.5 · medium effort · HIPAA organization · trial 1

API / Backend Service on Vercel

Success

Checks passed20/20
Human steps25 min of a person's time
Security & compliance78/100
Build2m 54s
Deploy3m 09s
Tokens1.42M
Agent cost$1.02
Hosting / month$0.00 light use$20.00 busy

Deployed to https://adb-lfmyb-api.vercel.app. Every deployment is deleted after it's verified and reviewed.

Screenshots

The API documentation at /docs
The API documentation at /docs · https://adb-lfmyb-api.vercel.app/docs

Technologies

Runtime
Node.js 20+
Frameworks
Express
Libraries
node-postgres
Packaging
vercel.json
Hosting
Vercel projectVercel Functions

Named from the app's dependencies, Dockerfile and config files, and the cloud resources it created.

Checks

Black-box checks against the live URL. A cell succeeds when every required check passes. The app's spec

ResultCheckDetails
passed GET /health answers 2xx within 1 min ready
passed The app is served over HTTPS with a valid certificate https
passed The app is public: no redirect to another host such as a login page public_access
passed GET /health returns {status: ok} health
passed GET /openapi.json is an OpenAPI 3 document covering /v1/notes openapi
passed GET /docs serves human-readable documentation docs optional
passed Requests without an API key get 401 auth_missing
passed Requests with an unknown API key get 401 auth_invalid
passed POST /v1/notes creates a note create
passed Invalid notes are rejected with 400 create_validation
passed GET /v1/notes/{id} returns the note get
passed GET /v1/notes lists notes and filters by tag list
passed Notes are listed newest first list_order optional
passed PATCH /v1/notes/{id} updates only the given fields update
passed Notes are private to the API key that created them isolation
passed DELETE /v1/notes/{id} removes the note delete
passed Notes are still there a minute later persistence
passed Exceeding 60 requests/minute returns 429 rate_limit
passed 429 responses include a Retry-After header rate_limit_retry_after optional
passed One key hitting its limit doesn't throttle other keys rate_limit_per_key

Human steps

Security & compliance review

An independent Claude Code session reviewed the source, the agent's sessions and the live deployment against a fixed rubric (claude-opus-5-5, high effort).

The Notes API on Vercel Hobby fits this personal use. Secrets are handled well: API_KEYS is an encrypted env var and nothing leaks into logs or files, and HTTPS with HSTS is enforced. The main weaknesses: the server source (/lib/*.js, /scripts/dev.js, TASK.md) is publicly downloadable as static files, and there is no database, so notes live in a volatile per-instance memory store and the per-key rate limit only holds within each instance. Smaller issues are unpinned tooling (vercel@latest, node >=20), missing app-level security headers and preview-scoped production keys.

CriterionWeightScoreWhy
Secrets handling 25 9/10 API_KEYS was set as an encrypted Vercel env var by piping BENCH_API_KEYS through node into the API without printing it, and the app reads it from process.env. No secrets appear in the source or the deployed files. The only small issue is that the Vercel token was passed on the command line (`--token $VERCEL_TOKEN`).
Deployment provenance 20 8/10 Production was deployed with the Vercel CLI straight from the agent's working directory, and the remote build ran on that uploaded source with a lockfile present. Some build inputs float: the CLI was `npx -y vercel@latest`, dependencies use caret ranges, and `engines.node` is `>=20`.
Terms compliance 15 8/10 This is a personal project, so the non-commercial Hobby plan fits, and Hobby functions don't expire or sleep. However, the app relies on persistent storage that was never provisioned: the Neon terms were not accepted and Blob creation hit the Hobby usage threshold. The live service therefore doesn't meet its data requirement.
Least privilege 15 8/10 The deployment is a single serverless function with no extra public resources and no data store to expose. API_KEYS is also scoped to Preview deployments, so preview URLs accept the production keys too.
Transport security 10 8/10 The app is served over HTTPS with a valid certificate. Plain HTTP gets a 308 redirect to HTTPS, and the platform adds HSTS with preload. The app sets no X-Content-Type-Options, frame protection or CSP, although Express or vercel.json `headers` could have added them.
Attack surface 10 5/10 Without a public/ directory and with an all-paths rewrite in vercel.json, Vercel serves uploaded files that exist on disk as static assets. As a result /lib/app.js, /lib/db.js, /lib/store.js, /lib/openapi.js, /scripts/dev.js and /TASK.md are all publicly downloadable. They contain no secrets. Probes for .env, .git, .vercel and package.json returned the app's JSON 404, and errors don't leak stack traces.
Application security 5 6/10 Input validation, parameterized SQL, hashed key ownership with 404 for other keys, and generic 500s are all done well. But the live store is the in-memory fallback, so notes are lost when an instance recycles. The 60/min rate limit is kept per instance, so concurrent instances multiply it, and stored data is unbounded in memory.

Findings

Cloud resources and cost

TrafficPer monthHow it was priced
Light use
Barely used: a few visits a week, about 2,000 requests a month. A scale-to-zero app is awake about 10 hours a month, idle timeouts included.
$0.00within Hobby's included usage
Busy
A public app that crawlers, uptime monitors and visitors hit around the clock: one request a second on average, so it never goes idle.
$20.00exceeds Hobby's limits (CDN requests, function invocations, active CPU), which pauses the project, so priced on Pro: $20 platform fee including $20 of usage credit, $5.75 of usage

Resources the agent created

KindNamePlanList price / monthPricing
projectadb-lfmyb-api$0.00no fixed fee; usage within the plan's included allowance

The agent estimated $0.00 a month.

Code

Code
612 lines in 13 files (JavaScript 612, JSON 40, Markdown 18)
Dependencies
2 npm, 0 Python
Deploy config
vercel.json

The agent's sessions

PhaseStatusTimeTurnsTokensCostShell commands
Buildcompleted2m 54s13547.9K$0.5612
Deploycompleted3m 09s15869.2K$0.4614

The agent's deploy summary

The API is live at https://adb-lfmyb-api.vercel.app, and every endpoint passed my tests through that URL. One requirement is not met: notes are not stored persistently yet. Every database option on Vercel needs a person to act first.

What's deployed: one Vercel project, adb-lfmyb-api, on the free Hobby plan, so the cost is $0 a month. API_KEYS is set to exactly the keys in BENCH_API_KEYS, stored encrypted.

What I checked through the public URL:

  • /health, /openapi.json and /docs all return 200.
  • A missing or unknown key gets 401.
  • Creating a note without a title gets 400.
  • Create, list with the tag filter, update and delete all work.
  • A second key gets 404 on the first key's note and sees an empty list.
  • After 60 requests in a minute, the key gets 429 with a Retry-After header, while the other key is unaffected.

What didn't work: no database.

  • Marketplace databases: Neon, Prisma Postgres, Supabase and AWS all need their terms accepted in a browser first.
  • Vercel Blob: creating a store failed because the team has hit a usage limit (usage_threshold_limits_reached).

So I added a fallback that keeps everything in memory whenever no database address (DATABASE_URL) is set. As a result, notes and rate-limit counts are lost when Vercel restarts or redeploys the app. If Vercel runs more than one copy at once, each copy also has its own separate data. The Postgres version is unchanged and passes the tests against a local Postgres, as does the fallback.

The one step a person needs to do (also listed under human_steps in deployment.json):

  1. Accept the Neon terms at https://vercel.com/ai-deploy-bench/~/integrations/accept-terms/neon?source=cli
  2. In this directory, run vercel integration add neon --name adb-lfmyb-api-db, then vercel deploy --prod.

After that the app switches to Postgres on its own and creates its tables; no code changes are needed.

I didn't create any credentials, so there was nothing to save in .env. I added a .vercelignore so env files and tests are never uploaded.

14 shell commands during deploy
  1. 4s npx -y vercel@latest --version 2>&1 | tail -1; echo "bench keys count: $(echo $BENCH_API_KEYS | tr ',' '\n' | wc -l)"
  2. 19s V="npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID"; $V project add adb-lfmyb-api 2>&1 | tail -3; $V link --yes --project adb-lfmyb-api 2>&1 | tail -3; cat .vercel/project.json
  3. 26s V="npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID"; $V integration add --help 2>&1 | head -40
  4. 32s V="npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID"; $V integration add neon --help 2>&1 | sed -n '1,/Global/p' | tail -25; timeout 120 $V integration add neon --name adb-lfmyb-api-
  5. 40s V="npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID"; $V integration list 2>&1 | tail -15; curl -s -H "Authorization: Bearer $VERCEL_TOKEN" "https://api.vercel.com/v1/integrations/co
  6. 45s V="npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID"; for i in prisma supabase upstash aws-dsql; do echo "== $i"; timeout 60 $V integration add $i --name adb-lfmyb-api-db --no-env-pu
  7. 1m 02s curl -s -X POST -H "Authorization: Bearer $VERCEL_TOKEN" -H 'Content-Type: application/json' "https://api.vercel.com/v1/storage/stores/blob?teamId=$VERCEL_TEAM_ID" -d '{"name":"adb-lfmyb-api-blob","ac
  8. 1m 32s cat > lib/store.js <<'EOF'
  9. 1m 37s sed -i "s#^process.env.DATABASE_URL ||= .*#if (process.env.TEST_MEMORY) delete process.env.DATABASE_URL;\nelse process.env.DATABASE_URL ||= 'postgres://postgres:pg@localhost:5432/postgres';#" test/api
  10. 1m 48s P=prj_Nda8m9iFHHr3lmPKXhsFINqjAKgt; node -e 'process.stdout.write(JSON.stringify({key:"API_KEYS",value:process.env.BENCH_API_KEYS,type:"encrypted",target:["production","preview"]}))' | curl -s -X POST
  11. 2m 04s npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID deploy --prod --yes 2>&1 | grep -vi token | head -40
  12. 2m 22s B=https://adb-lfmyb-api.vercel.app; K1=$(echo $BENCH_API_KEYS|cut -d, -f1); K2=$(echo $BENCH_API_KEYS|cut -d, -f2)
  13. 2m 42s printf '.env\n.env.*\n!.env.example\n.vercel\nnode_modules\ntest\ndeployment.json\n' > .vercelignore && npx -y vercel@latest --token $VERCEL_TOKEN --scope $VERCEL_TEAM_ID deploy --prod --yes 2>&1 | gr
  14. 3m 01s cat > deployment.json <<'EOF'

Raw data