Job13

API & MCP · v1 · eCFR edition 2026-01-01

Your agent can call this
and get the citation back.

The same engine behind the site, as a JSON API and a hosted MCP server. A published sandbox key means no signup step to get stuck on — a model that reads this page can make a correct, cited call on its first attempt.

Public sandbox key — no signup

lb_test_sandbox

Send it as Authorization: Bearer lb_test_sandbox. Shared and rate-limited to 25 requests per 24 hours per IP. The counter lives in memory, so it also resets whenever the deployment restarts — fine for evaluation, not for production.

Authentication

One header, one key

Every endpoint takes Authorization: Bearer <key>. An unrecognised key is rejected with a structured 401 rather than silently downgraded to the sandbox tier — a caller with a stale key must be told, not quietly served under different limits.

# Base URL
https://www.job13.com/api/v1

# Every request
-H 'authorization: Bearer lb_test_sandbox'

CORS is open for GET and POST from any origin, so browser-side agents can call it directly.

Endpoints

Six routes, all cited

Every command below is complete and runnable as written. Responses are trimmed for readability but the field names are exact; each one also carries rule_edition and disclaimer.

POST/api/v1/recordability

Decide a case

The primary endpoint. Returns the verdict, the 1904.7(a) criteria met, and the ordered decision trail with a verbatim citation on every decisive step. Only occurredInWorkEnvironment and isNewCase are required; anything you omit is treated as unknown, which can produce needs_review rather than a guess.

curl -s https://www.job13.com/api/v1/recordability \
  -H 'authorization: Bearer lb_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{
    "occurredInWorkEnvironment": "yes",
    "isNewCase": "yes",
    "treatments": ["sutures_staples_or_surgical_glue"]
  }'
{
  "verdict": "recordable",
  "summary": "Recordable — the case meets the 1904.7(a) criterion: Medical treatment beyond first aid.",
  "criteriaMet": ["Medical treatment beyond first aid"],
  "steps": [
    {
      "key": "general_criteria",
      "question": "Does the case meet one or more of the general recording criteria in 1904.7(a)?",
      "answer": "Yes — Medical treatment beyond first aid.",
      "decisive": true,
      "citations": [
        {
          "section": "1904.7",
          "paragraph": "1904.7(b)(5)",
          "quote": "Not on the exhaustive first-aid list; only butterfly bandages and Steri-Strips are first aid — 1904.7(b)(5)(ii)(D).",
          "url": "https://www.ecfr.gov/current/title-29/part-1904/section-1904.7"
        }
      ]
    }
  ],
  "unresolved": [],
  "caveats": [],
  "rule_edition": "2026-01-01",
  "disclaimer": "Job13 is an administrative classification aid, not legal advice. ..."
}
POST/api/v1/rates

Compute TRIR, DART and DAFW

Incidence rates on the standard 200,000-hour base for the 300A summary. hoursWorked is required and may be 0 — that is the case the null-not-zero invariant exists for, so it must be expressible.

curl -s https://www.job13.com/api/v1/rates \
  -H 'authorization: Bearer lb_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{
    "hoursWorked": 400000,
    "totalRecordableCases": 6,
    "casesWithDaysAway": 2,
    "casesWithRestrictionOrTransfer": 1
  }'
{
  "trir": 3,
  "dart": 1.5,
  "dafw": 1,
  "basis_hours": 200000,
  "note": "Rates are per 200,000 hours worked ...",
  "formulas": { "trir": "(total recordable cases x 200000) / hours worked", "...": "..." }
}

// With hoursWorked: 0 —
{
  "trir": null,
  "dart": null,
  "dafw": null,
  "note": "hoursWorked is zero or missing, so no rate can be computed. These fields are null rather than 0 on purpose: 0 would read as a perfect safety record when the truth is that there is no data."
}
GET/api/v1/rule/{section}

Read the regulation verbatim

One section of 29 CFR Part 1904, straight from the parsed eCFR corpus. A 404 returns every valid section id in known_keys.

curl -s https://www.job13.com/api/v1/rule/1904.7 -H 'authorization: Bearer lb_test_sandbox'
{
  "section": "1904.7",
  "title": "General recording criteria",
  "heading": "§ 1904.7 General recording criteria.",
  "paragraphs": ["(a) Basic requirement. You must consider an injury or illness to meet the general recording criteria, and therefore to be recordable, if it results in any of the following: death, days away from work, ..."],
  "url": "https://www.ecfr.gov/current/title-29/part-1904/section-1904.7",
  "verbatim": true
}
GET/api/v1/first-aid-list

The exhaustive first-aid list

All 14 items of 1904.7(b)(5)(ii)(A)-(N), verbatim. The list is exhaustive: a treatment that is not on it is medical treatment beyond first aid.

curl -s https://www.job13.com/api/v1/first-aid-list -H 'authorization: Bearer lb_test_sandbox'
{
  "count": 14,
  "exhaustive": true,
  "items": [
    { "letter": "A", "paragraph": "1904.7(b)(5)(ii)(A)", "text": "Using a non-prescription medication at nonprescription strength ..." },
    { "letter": "D", "paragraph": "1904.7(b)(5)(ii)(D)", "text": "Using wound coverings such as bandages, Band-Aids™, gauze pads, etc.; or using butterfly bandages or Steri-Strips™ ..." }
  ]
}
GET/api/v1/exemptions?naics=4412

Check the 1904.2 industry exemption

Partial-exemption status for a NAICS code. Omit the parameter to get the full Appendix A list. Partial exemption never removes the 1904.39 duty to report fatalities and severe injuries.

curl -s 'https://www.job13.com/api/v1/exemptions?naics=4412' -H 'authorization: Bearer lb_test_sandbox'
{
  "naics": "4412",
  "partially_exempt": true,
  "description": "Other Motor Vehicle Dealers.",
  "summary": "NAICS 4412 (Other Motor Vehicle Dealers.) is listed in Appendix A to Subpart B, so this establishment is partially exempt ...",
  "caveat": "Partial exemption changes the DUTY TO RECORD, not whether a case meets the recording criteria ..."
}
GET/api/v1/treatments

The treatment vocabulary

Every treatment code /recordability accepts, with its classification and the rationale in the rule's own terms. Free text is rejected on purpose — call this to build a valid request.

curl -s https://www.job13.com/api/v1/treatments -H 'authorization: Bearer lb_test_sandbox'
{
  "count": 27,
  "treatments": [
    { "code": "wound_coverings_butterfly_steristrips", "kind": "first_aid", "first_aid_letter": "D", "label": "Wound coverings (bandages, gauze, butterfly bandages, Steri-Strips)" },
    { "code": "sutures_staples_or_surgical_glue", "kind": "medical_treatment", "first_aid_letter": null, "rationale": "Not on the exhaustive first-aid list; only butterfly bandages and Steri-Strips are first aid — 1904.7(b)(5)(ii)(D)." },
    { "code": "other_unlisted", "kind": "unknown", "rationale": "The first-aid list at 1904.7(b)(5)(ii) is exhaustive ..." }
  ],
  "work_relatedness_exceptions": [{ "numeral": "i", "paragraph": "1904.5(b)(2)(i)", "text": "At the time of the injury or illness, the employee was present in the work environment as a member of the general public rather than as an employee." }]
}

Model Context Protocol

Hosted MCP server

JSON-RPC 2.0 over HTTP (Streamable HTTP transport) at https://www.job13.com/api/mcp. Stateless — no session to resume, so a dropped connection is a non-event. Implements initialize, tools/list, tools/call and ping.

{
  "mcpServers": {
    "job13": {
      "type": "http",
      "url": "https://www.job13.com/api/mcp",
      "headers": {
        "Authorization": "Bearer lb_test_sandbox"
      }
    }
  }
}
  • check_recordability

    Full 1904 determination with citations. Returns needs_review when the rule needs a fact you have not established.

  • compute_incidence_rate

    TRIR / DART / DAFW on the 200,000-hour base. Null, never 0, without hours.

  • get_1904_rule

    Verbatim text of a Part 1904 section.

  • check_industry_exemption

    1904.2 Appendix A status for a NAICS code.

  • list_first_aid_treatments

    The exhaustive (A)-(N) list plus the treatment vocabulary.

Every tool returns a readable text block carrying the citations inline, plus the raw object as structuredContent. The text exists so a model that paraphrases the verdict to a human still carries the provision with it.

curl -s https://www.job13.com/api/mcp \
  -H 'authorization: Bearer lb_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -s https://www.job13.com/api/mcp \
  -H 'authorization: Bearer lb_test_sandbox' \
  -H 'content-type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "check_recordability",
      "arguments": {
        "occurredInWorkEnvironment": "yes",
        "isNewCase": "yes",
        "treatments": ["sutures_staples_or_surgical_glue"]
      }
    }
  }'

Protocol errors follow JSON-RPC: -32601 for an unknown method, -32602 for bad params — and a -32602 carries the same known_keys the REST API would return.

Errors

Every error is self-correcting

Errors are always {"error": {"code", "message", "docs_url", ...}}. When the offending field is an enum, the body carries known_keys — the complete list of accepted values — so a failed call can be fixed on the next attempt without a human reading these docs.

$ curl -s https://www.job13.com/api/v1/recordability \
    -H 'authorization: Bearer lb_test_sandbox' \
    -H 'content-type: application/json' \
    -d '{"occurredInWorkEnvironment":"yes","isNewCase":"yes","treatments":["stitches"]}'

{
  "error": {
    "code": "invalid_request",
    "message": "`treatments[0]` is not a recognised value.",
    "docs_url": "https://www.job13.com/docs/api#errors",
    "field": "treatments[0]",
    "received": "stitches",
    "known_keys": [
      "nonprescription_medication_nonprescription_strength",
      "tetanus_immunization",
      "...",
      "sutures_staples_or_surgical_glue",
      "other_unlisted",
      "none"
    ]
  }
}
CodeHTTPMeaning
invalid_request400A field is missing, the wrong type, or not in the enum. Carries `field`, and `known_keys` whenever the field is an enum.
unauthorized401Missing, malformed or unrecognised bearer key. Carries `sandbox_key` so you can retry with the public key.
rate_limited429More than 25 sandbox requests from this IP in 24 hours. Carries `retry_after_seconds` and a `Retry-After` header.
not_found404No such rule section. Carries `known_keys` with every valid section id.
method_not_allowed405Wrong verb for the endpoint. Carries `allowed_methods`.
idempotency_key_reuse409The same Idempotency-Key arrived with a different request body.
internal_error500Unexpected server fault. Safe to retry with the same Idempotency-Key.

Idempotency

Retries are safe

Send an Idempotency-Key header on any POST. A replay with the same key and the same body returns the byte-identical original response, plus Idempotency-Replayed: true — so a caller that lost the first reply can tell a repeat from a fresh result. Reusing a key with a different body is a 409 rather than a silent wrong answer. Keys are held for 24 hours in memory, per instance.

curl -s https://www.job13.com/api/v1/recordability \
  -H 'authorization: Bearer lb_test_sandbox' \
  -H 'content-type: application/json' \
  -H 'idempotency-key: case-2026-0417-a' \
  -d '{"occurredInWorkEnvironment":"yes","isNewCase":"yes","treatments":["none"]}'

# Run it a second time with the same key:
#   → identical body, byte for byte
#   → Idempotency-Replayed: true

Rate limits

Headers on every response

The sandbox key allows 25 requests per rolling 24 hours per IP. Every response — success or error — carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds). A 429 adds Retry-After. The counter is in-memory and per-instance, so it resets on deploy; account keys will use a durable store. See pricing for production volumes.

Contract

What this API will not do

It will not guess

Anything unknown that could change the outcome produces verdict: needs_review with an unresolved[] list naming exactly what to establish. That is a 200, not an error — branch on verdict, never on the status code.

It will not report 0 for no data

With no hours worked, TRIR, DART and DAFW are null and note says why. A 0.00 TRIR from missing data reads as a perfect safety record.

It will not paraphrase the rule

Every quote comes verbatim from the parsed eCFR corpus. If a claim cannot be quoted, the API does not make it — and it never invents a section that does not exist.

Job13is an administrative classification aid, not legal advice. Recordkeeping duties remain the employer’s under 29 CFR Part 1904.