agentsclimarketplace

Api validation

Skill krzemienski/validationforge/skills/api-validation

Use whenever validating an HTTP API (REST, GraphQL-over-HTTP, webhook receiver) — before deployment, after endpoint or auth changes, when onboarding a new API consumer, or when answering 'does this API actually work end-to-end'. Covers health checks, full CRUD cycles, auth (valid/invalid/missing tokens, 401/403), error response shape, pagination, and rate limiting, all via curl with JSON bodies and status codes saved as evidence. Reach for it on phrases like 'test the API', 'validate the endpoints', 'check auth', 'curl the server', 'does /api/... work'.From its SKILL.md

Install
npx -y skills add krzemienski/validationforge --skill api-validation

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.3 KB, as published. Nobody here has run it

API Validation

Quick start

Work this list in order. If Health + CRUD + Auth all PASS, most APIs are validated — you only need steps 4-6 when they apply.

#StepTimeSkip when
1Health check5 minnever
2CRUD cycle (per resource)~20 minAPI is read-only
3Auth (valid / invalid / missing token)~10 minAPI has no auth
4Error response shape10 minnever (even if auth/CRUD pass, error bodies often lie)
5Pagination10 minno list endpoints
6Rate limiting5 minAPI docs don't mention rate limits

Prerequisites

Verify every row before starting — each is a gate, not a hint. If any fails, fix that first or the rest of the run is noise.

RequirementHow to verify
API server runningcurl -s http://localhost:PORT/health
Database seeded (if applicable)Check server startup logs for seed confirmation
Auth tokens available (if applicable)Login endpoint returns token
jq installedjq --version
Evidence directory existsmkdir -p e2e-evidence

Common failures (read this before debugging)

Most "API validation is broken" reports trace back to one of these. Check here before diving into the protocol steps.

SymptomLikely CauseFix
Connection refusedServer not running or wrong portStart server, check PORT env var
500 Internal Server ErrorUnhandled exception in route handlerCheck server logs for stack trace
Empty response bodyHandler returns status without bodyAdd response body to handler
CRUD read-after-delete returns 200Soft delete not filtering correctlyCheck delete implementation and query filters
Auth token not returnedLogin handler missing token generationCheck auth service and token signing
Pagination returns duplicatesMissing ORDER BY in queryAdd deterministic ordering to list queries

Step 1: Health Check

curl -s http://localhost:PORT/health \
  | tee e2e-evidence/api-health.json | jq .

Verify the response includes expected fields (status, version, uptime, etc.). A bare 200 OK without body content is suspicious — document it.

Step 2: CRUD Validation Pattern

Prerequisite: $TOKEN must be set before Step 2. The Create/Read/Update/Delete requests below all send Authorization: Bearer $TOKEN. The token is obtained in Step 3 ("Valid credentials"). If you are running steps individually — or the API requires auth on every CRUD endpoint — run Step 3's login request first, export TOKEN=..., then return here. If the API is unauthenticated, drop the -H "Authorization: Bearer $TOKEN" header from the snippets below.

Fast path: If you just want to run the whole CRUD cycle, bash scripts/crud-validator.sh --base-url=http://localhost:PORT --resource=posts --token=$TOKEN --evidence-dir=e2e-evidence/api does steps 2.1–2.7 in one shot. The inline steps below exist for the cases where you need to inspect or modify a single step.

For each resource in the API, execute the full CRUD cycle. Save every response.

Create

curl -s -X POST http://localhost:PORT/api/RESOURCE \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "Test Item", "description": "Created by validation"}' \
  | tee e2e-evidence/api-create-RESOURCE.json | jq .

# Extract the created ID for subsequent operations
RESOURCE_ID=$(jq -r '.id // .data.id' e2e-evidence/api-create-RESOURCE.json)
echo "Created ID: $RESOURCE_ID"
[ -z "$RESOURCE_ID" ] || [ "$RESOURCE_ID" = "null" ] && { echo "ERROR: Create failed, cannot continue CRUD cycle" >&2; exit 1; }

Read (single)

curl -s http://localhost:PORT/api/RESOURCE/$RESOURCE_ID \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-read-RESOURCE.json | jq .

Read (list)

curl -s http://localhost:PORT/api/RESOURCE \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-list-RESOURCE.json | jq .

Update

curl -s -X PUT http://localhost:PORT/api/RESOURCE/$RESOURCE_ID \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name": "Updated Item", "description": "Modified by validation"}' \
  | tee e2e-evidence/api-update-RESOURCE.json | jq .

Verify update persisted

curl -s http://localhost:PORT/api/RESOURCE/$RESOURCE_ID \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-read-after-update-RESOURCE.json | jq .

Delete

curl -s -X DELETE http://localhost:PORT/api/RESOURCE/$RESOURCE_ID \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-delete-RESOURCE.json | jq .

Verify delete

curl -s -w "\nHTTP_STATUS:%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:PORT/api/RESOURCE/$RESOURCE_ID \
  | tee e2e-evidence/api-read-after-delete-RESOURCE.txt

Expected: 404 response.

Step 3: Authentication Testing

Fast path: bash scripts/auth-test.sh --base-url=http://localhost:PORT --login-endpoint=/auth/login --protected-endpoint=/api/protected [email protected] --password=validpassword --evidence-dir=e2e-evidence/api runs the four core auth checks (valid login, authenticated request, invalid token → 401, no token → 401) and writes all evidence in one shot. The inline steps below remain the pedagogical source of truth.

Valid credentials

TOKEN=$(curl -s -X POST http://localhost:PORT/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email": "[email protected]", "password": "validpassword"}' \
  | tee e2e-evidence/api-auth-valid-login.json | jq -r '.token // .access_token')
echo "Token: $TOKEN"

Authenticated request

curl -s -H "Authorization: Bearer $TOKEN" http://localhost:PORT/api/protected \
  | tee e2e-evidence/api-auth-protected-access.json | jq .

Expired/invalid token

curl -s -w "\nHTTP_STATUS:%{http_code}" \
  -H "Authorization: Bearer invalid.token.here" \
  http://localhost:PORT/api/protected \
  | tee e2e-evidence/api-auth-invalid-token.txt

Expected: 401 with error message body.

No token

curl -s -w "\nHTTP_STATUS:%{http_code}" http://localhost:PORT/api/protected \
  | tee e2e-evidence/api-auth-no-token.txt

Expected: 401.

Wrong role (if RBAC exists)

curl -s -w "\nHTTP_STATUS:%{http_code}" \
  -H "Authorization: Bearer $USER_TOKEN" \
  http://localhost:PORT/api/admin-only \
  | tee e2e-evidence/api-auth-wrong-role.txt

Expected: 403.

Step 4: Error Response Validation

Test that error responses include proper status codes AND meaningful bodies. This is the step that most often catches real bugs — a 422 response that APIs claim "works" because the status code is right, but has a useless body, silently breaks every frontend that reads it.

Good error response (actionable, machine-readable AND human-readable):

{
  "error": "Validation failed",
  "errors": [
    {"field": "email", "message": "Invalid email format"},
    {"field": "password", "message": "Must be at least 8 characters"}
  ]
}

Bad error responses (all of these fail validation):

{"error": 1008}                           // numeric code only, no message
{}                                         // empty object
{"errors": []}                             // empty errors array — validation said what?
{"message": "Something went wrong"}       // not actionable, no field info

The rule: a human reading the response should know what to fix. A frontend parsing the response should know which field is wrong.

# 400 — Bad request (missing required field)
curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST http://localhost:PORT/api/RESOURCE \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{}' \
  | tee e2e-evidence/api-error-400.txt

# 404 — Not found
curl -s -w "\nHTTP_STATUS:%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:PORT/api/RESOURCE/nonexistent-id \
  | tee e2e-evidence/api-error-404.txt

# 422 — Validation error (invalid data)
curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST http://localhost:PORT/api/RESOURCE \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"email": "not-an-email"}' \
  | tee e2e-evidence/api-error-422.txt

Every error response MUST include a human-readable message, not just a status code.

Step 5: Pagination

# First page
curl -s "http://localhost:PORT/api/RESOURCE?limit=2&offset=0" \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-pagination-page1.json | jq .

# Second page
curl -s "http://localhost:PORT/api/RESOURCE?limit=2&offset=2" \
  -H "Authorization: Bearer $TOKEN" \
  | tee e2e-evidence/api-pagination-page2.json | jq .

Verify:

  • Page 1 and Page 2 return different items
  • Total count (if included) is consistent
  • Empty page returns empty array, not error

Step 6: Rate Limiting (if applicable)

Skip this step unless the API docs mention rate limits, or you can see rate-limit middleware in the code (Express express-rate-limit, @fastify/rate-limit, Django's ratelimit, etc.). If rate limiting isn't configured, hammering the endpoint just produces noise.

for i in $(seq 1 20); do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:PORT/api/RESOURCE)
  echo "Request $i: $STATUS" | tee -a e2e-evidence/api-rate-limit-test.txt
done

If rate limiting exists, expect 429 status after the threshold with a Retry-After header. Without the header, clients don't know when to retry — that's a FAIL even if 429 is returned.

Evidence Standards

GOOD: Full JSON response body saved to file, status code captured, response describes what data was returned.

BAD: "API returned 200" or "Request succeeded" without saving the response body.

Every evidence file must contain the FULL response body — not just a status code.

PASS Criteria Template

  • Health endpoint returns 200 with expected body structure
  • Create returns 201 with created resource (including ID)
  • Read returns 200 with correct resource data
  • Update returns 200 with modified data, changes persist on re-read
  • Delete returns 200/204, subsequent read returns 404
  • Auth: valid credentials return token, invalid return 401
  • Auth: protected endpoints reject unauthenticated requests
  • Error responses include status code AND human-readable message body
  • Pagination returns correct subsets with consistent totals
  • Response times under 500ms for single-resource operations

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.