Docs

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.pdf

The 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.json

A 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.

StatusAction
400Correct the request shape, cursor, or idempotency header.
401Check key expiry, revocation, and the owner's current verified Admin access.
403Check scope and workspace pilot access.
405Use a method listed in the Allow response header.
404Check the workspace, ID, deletion, or preparation expiry.
409Reconcile the current resource state, quote, and original request body.
422Check file content/size, available quota/credits, and active work limits.
429Wait for Retry-After before retrying the same logical request.
500/503Retry 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".

On this page