API quickstart
Prepare a resume, submit analysis safely, and retrieve its report.
Start with authentication and scopes and the developer sandbox if this is your first integration.
Use an enabled pilot workspace and a server-side key with analyses:write and reports:read. These examples use curl and jq. Set PROBB_URL to your application's origin and PROBB_API_KEY through your secret manager.
Prepare and upload
Use a unique, stable request key for each logical preparation. Retry the same body with that same key after a network error. Keys accept 1–128 letters, digits, ., _, :, and -.
export PROBB_URL='https://your-probb-app.example'
# PROBB_API_KEY is supplied by your secret manager.
SIZE=$(wc -c < resume.pdf | tr -d ' ')
jq -n --argjson size "$SIZE" '{
fileName: "resume.pdf", fileSize: $size, mimeType: "application/pdf",
jobDescription: {kind: "text", text: "Build and maintain our backend services."}
}' > prepare.json
curl --fail-with-body "$PROBB_URL/api/v1/uploads" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: candidate-42-upload-1' \
--data-binary @prepare.json > upload.json
REPORT_ID=$(jq -r '.data.reportId' upload.json)
UPLOAD_URL=$(jq -r '.data.upload.url' upload.json)
curl --fail-with-body -X PUT "$UPLOAD_URL" \
-H 'Content-Type: application/pdf' --data-binary @resume.pdfThe preparation returns 201. The storage URL is a credential: keep it private and do not send your API key to storage. To use a saved job description, replace the jobDescription value with {"kind":"existing","id":"YOUR_JOB_DESCRIPTION_ID"}. It must be active and belong to the same workspace.
Before admission, refresh an expired storage URL with POST /api/v1/uploads/{reportId}/url using the same Bearer key. The preparation itself expires after 24 hours. After admission, refresh returns 409; replaying preparation returns the same report ID with upload: null.
Quote and submit
jq -n --arg id "$REPORT_ID" '{reportId:$id}' > quote-input.json
curl --fail-with-body "$PROBB_URL/api/v1/analysis-quotes" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H 'Content-Type: application/json' --data-binary @quote-input.json > quote.json
FINGERPRINT=$(jq -r '.data.fingerprint' quote.json)
jq -n --arg id "$REPORT_ID" --arg fp "$FINGERPRINT" \
'{reportId:$id,quoteFingerprint:$fp}' > admission.json
curl --fail-with-body "$PROBB_URL/api/v1/analyses" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: candidate-42-analysis-1' \
--data-binary @admission.json > admission-result.jsonA quote describes the assigned report version and applicable credit units/scale. It does not reserve capacity. Admission returns 202 with reportId, attemptId, and statusUrl; the Location header also provides the status path. Quota or credits are checked at admission. The accepted file is frozen before processing, so later writes to the temporary upload URL cannot alter that attempt.
Concurrent retries with the same admission key and identical body return one admitted attempt. Reusing a request key with changed input returns 409. Receipts are scoped to the workspace and operation, and persist across key rotation and report deletion. Never switch to a fresh request key merely because the network timed out.
If a quote is stale, fetch a new quote and submit with a new admission key. After terminal failure, an intentional new attempt requires a new key and current quote. A completed analysis cannot be restarted through this API. A candidate/job mismatch requires an authorised person to review it in the app.
Read status and results
curl --fail-with-body "$PROBB_URL/api/v1/reports/$REPORT_ID/status" \
-H "Authorization: Bearer $PROBB_API_KEY"
curl --fail-with-body "$PROBB_URL/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $PROBB_API_KEY"
curl --fail-with-body "$PROBB_URL/api/v1/reports?limit=25" \
-H "Authorization: Bearer $PROBB_API_KEY"Responses wrap results in data. List responses include items and nextCursor; send that opaque cursor with the same date filters for the next page. Reports are ordered newest first. Status identifies the current attempt; a delayed webhook may refer to an older attempt. Pending results are null. See report evidence for choosing summary, status, or a detailed result, and the API reference for fields and filtering.
Errors and limits
Errors contain error.code, message, and requestId. Keep the request ID for support, without sharing your key or resume. Responses use Cache-Control: no-store.
| Status | Action |
|---|---|
| 400 | Correct the request shape, cursor, or idempotency header. |
| 401 | Check key expiry, revocation, and the owner's current verified Admin access. |
| 403 | Check scope and workspace pilot access. |
| 405 | Use a method listed in the Allow response header. |
| 404 | Check the workspace, ID, deletion, or preparation expiry. |
| 409 | Reconcile the current resource state, quote, and original request body. |
| 422 | Check file content/size, available quota/credits, and active work limits. |
| 429 | Wait for Retry-After before retrying the same logical request. |
| 500/503 | Retry with backoff and the original idempotency key. |
Fixed one-minute limits are 60 reads and ten writes per key, 120 reads and 20 writes per workspace, 300 ingress requests per IP, and 30 invalid-auth attempts per IP. A workspace may hold 20 pending preparations. API admission allows at most five active resume analyses across API and app submissions. Poll conservatively or use webhooks.
HTTP methods
Report read routes support GET, authenticated HEAD, and OPTIONS. Write routes support POST and OPTIONS. OPTIONS returns 204 with an Allow header and an empty body; it does not enable browser CORS. Unsupported methods return 405 with Allow, a METHOD_NOT_ALLOWED error, and a request ID. HEAD responses never contain a body, including errors. Method discovery and rejected methods count toward the ingress IP limit, but do not consume authenticated write quota. A 401 response includes WWW-Authenticate: Bearer realm="probb-api".