Developer Reference · REST API · v1

REST API reference.

A single, free endpoint for generating professional stamp images in SVG, PNG, or PDF. No authentication required. Engineers, architects, surveyors, and landscape architects across all US states.

No auth CORS enabled SVG · PNG · PDF 4 professions · 50 states

Overview

The EngineeringID REST API provides a single, free endpoint for generating professional stamp images. No authentication required.

Use case. Generate professional stamps for engineers, architects, land surveyors, and landscape architects across all 50 US states. Returns SVG, PNG, or PDF — no account needed.

POST /v1/stamp

Generates a professional stamp image (seal graphic) for any licensed professional. No authentication required.

POST /v1/stamp
Base URL
https://api.engineeringid.com
Content-Type
application/json
application/x-www-form-urlencoded
Returns
image/svg+xml · image/png · application/pdf

Request

Body

{
  "name": "Jane Smith",
  "license": "PE-12345",
  "state": "california",
  "profession": "engineer",
  "format": "svg",
  "discipline": "CIVIL",
  "city": "Los Angeles",
  "date": "2026-05-08"
}

Parameters

Parameter Type Required Description
name String Yes Full name of the professional (e.g., "Jane Smith"). Must not be empty.
license String Yes Professional license or credential number (e.g., "PE-12345"). Format varies by profession and state.
state String Yes State name or abbreviation (e.g., "california", "CA", "new_york"). Case-insensitive.
profession String No Profession type. Defaults to "engineer". Supported: engineer (and subtypes like civil-engineer, structural-engineer, etc.), architect, land-surveyor, landscape-architect.
format String No Output format. Defaults to "png". Options: "svg" (vector), "png" (raster, 4× scale), "pdf" (print-ready, 8× scale).
discipline String No Engineering discipline (e.g., "CIVIL", "STRUCTURAL"). Only applicable to stamps that include a discipline field.
city String No City name. Only applicable to stamps that include a city field (e.g., some architect stamps).
state_label String No State abbreviation label on the stamp (e.g., "WIS."). Only for stamps with a separate state text field.
date String No Date string for stamps that include a date field (e.g., "01/15/26").
font_family String No CSS font-family stack. Default: "Helvetica, Liberation Sans, sans-serif". Allowed characters: letters, digits, spaces, commas, single quotes, hyphens, periods. Max 200 chars.
font_weight String No Default: "normal". Allowed: normal, bold, bolder, lighter, 100, 200, 300, 400, 500, 600, 700, 800, 900.
font_style String No Default: "normal". Allowed: normal, italic, oblique.
font_size String No Default: the source SVG's value (typically 12px). Number optionally followed by px, pt, em, rem, or %.
font_variant String No Allowed: normal, small-caps.
font_stretch String No Allowed: normal, condensed, expanded, ultra-condensed, extra-condensed, semi-condensed, semi-expanded, extra-expanded, ultra-expanded.
letter_spacing String No "normal" or a number with optional unit (px, pt, em, rem). Negative values allowed for tighter spacing.
word_spacing String No Same syntax as letter_spacing.
text_decoration String No Allowed: none, underline, line-through, overline.
text_anchor String No Horizontal anchor for the text relative to its (x, y). Allowed: start, middle, end. The reference SVGs use middle; overriding may misalign the inscription.
fill String No Glyph fill color. Default: "#000000". Allowed: 3/4/6/8-digit hex (#RGB, #RGBA, #RRGGBB, #RRGGBBAA), none, transparent, currentColor.
fill_opacity String No Decimal 0–1 (e.g., "0.5").
stroke String No Glyph stroke color. Default: "none". Same value syntax as fill.
stroke_width String No Number optionally followed by px, pt, em, or rem.
stroke_opacity String No Decimal 0–1.
stroke_linecap String No Allowed: butt, round, square.
stroke_linejoin String No Allowed: miter, round, bevel, miter-clip, arcs.
stroke_dasharray String No Comma-separated numbers (max 10), e.g., "5,2,5".
opacity String No Decimal 0–1. Applies to the entire glyph (fill + stroke composited together), distinct from fill_opacity / stroke_opacity.
Validation. All required fields must be non-empty strings. Profession is resolved against known profession types; unknown professions return a 400.

Response

200 Success

Returns the professional stamp in the requested format: SVG (vector), PNG (raster, 4× scale), or PDF (print-ready, 8× scale).

HTTP/1.1 200 OK
Content-Type: image/svg+xml           (or image/png, application/pdf)
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=3600
Content-Disposition: attachment; filename="stamp.svg"  (SVG and PDF only)

[SVG/PNG/PDF data]
  • Content-Type: image/png
  • Access-Control-Allow-Origin: * — CORS enabled for embedding in third-party sites
  • Cache-Control: public, max-age=3600 — Images cached for 1 hour

400 Bad Request

{
  "error": "bad_request",
  "message": "Missing required field: name"
}
  • Missing or empty required field (name, license, or state)
  • Unknown profession type

404 Not Found

{
  "error": "not_found",
  "message": "No stamp template available for this profession and state combination"
}

No stamp template exists for the requested profession in the given state. This may occur for less common professions or jurisdictions not yet supported.

500 Internal Server Error

{
  "error": "render_failed",
  "message": "Failed to generate stamp image"
}

An unexpected error occurred during stamp rendering. Please try again.

Stamp examples

cURL

# Get a PNG (default)
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Smith","license":"PE-12345","state":"california","profession":"engineer"}' \
  --output stamp.png

# Get an SVG
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Smith","license":"PE-12345","state":"california","format":"svg"}' \
  --output stamp.svg

# Get a PDF
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Smith","license":"PE-12345","state":"california","format":"pdf"}' \
  --output stamp.pdf

JavaScript / Node.js

const response = await fetch('https://api.engineeringid.com/v1/stamp', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Jane Smith',
    license: 'PE-12345',
    state: 'california',
    profession: 'engineer',
    format: 'svg'  // or 'png', 'pdf'
  })
});

const blob = await response.blob();
const url = URL.createObjectURL(blob);
document.getElementById('stamp').src = url;

Python

import requests

response = requests.post(
  'https://api.engineeringid.com/v1/stamp',
  json={
    'name': 'Jane Smith',
    'license': 'PE-12345',
    'state': 'california',
    'profession': 'engineer',
    'format': 'svg',
    'discipline': 'CIVIL',
  }
)

with open('stamp.svg', 'wb') as f:
  f.write(response.content)

Default profession

When profession is omitted, it defaults to "engineer":

# profession defaults to "engineer" when omitted
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{"name":"John Doe","license":"PE-98765","state":"texas"}' \
  --output stamp.png

Style overrides

All 19 style parameters are optional. Omitted properties fall back to the defaults (sans-serif font stack, normal weight, black fill, no stroke).

# Customize font + colors. All 19 style params are optional.
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "license": "PE-12345",
    "state": "california",
    "format": "png",
    "font_family": "Liberation Sans, sans-serif",
    "font_weight": "bold",
    "font_size": "14px",
    "fill": "#003366",
    "stroke": "none",
    "letter_spacing": "0.5px"
  }' \
  --output stamp.png

Validation error (400)

Invalid style values return a 400 with a message that names the field, echoes the value you sent, and lists what the field accepts:

# Sending an invalid value returns a 400 with a message that names
# the field, echoes what you sent, and lists what it accepts.
curl -X POST https://api.engineeringid.com/v1/stamp \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane","license":"PE-1","state":"CA","font_weight":"extra-bold"}'

# 400 Bad Request
# {
#   "error": "bad_request",
#   "message": "Invalid value for 'font_weight': \"extra-bold\". Allowed values: normal, bold, bolder, lighter, or 100/200/300/400/500/600/700/800/900."
# }

GET /v1/verify/:code

Look up a sealed document by its verification code. Returns document metadata, credential details, and seal information. No authentication required.

GET /v1/verify/:code
Auth
None
Returns
application/json
Rate limit
60 / min per IP

Parameters

Parameter In Type Required Description
code path String Yes 12-character lowercase alphanumeric verification code printed on the sealed document (e.g., "abc123def456").

200 Response — valid code

{
  "valid": true,
  "document": {
    "name": "Bridge Design Report.pdf",
    "sealed_at": "2026-05-01T14:23:07Z",
    "document_hash": "a1b2c3d4e5f6..."
  },
  "credential": {
    "holder_name": "Jane Smith",
    "license_type": "PE",
    "license_number": "PE-12345",
    "region_code": "CA",
    "status": "active"
  },
  "seal": {
    "verification_code": "abc123def456",
    "sealed_at": "2026-05-01T14:23:07Z"
  },
  "blockchain": null,
  "verification_url": "https://verify.engineeringid.com/abc123def456"
}

404 Response — code not found

{
  "valid": false,
  "error": "not_found",
  "message": "No sealed document found with this verification code"
}

cURL example

curl https://api.engineeringid.com/v1/verify/abc123def456

POST /v1/verify/:code/match

Compare a client-provided SHA-256 hash against the sealed original to detect tampering. Use this when the verifier has a copy of the document and wants to confirm it hasn't been modified since sealing. No authentication required.

POST /v1/verify/:code/match
Auth
None
Content-Type
application/json
Rate limit
60 / min per IP

Parameters

Parameter In Type Required Description
code path String Yes 12-character verification code from the sealed document.
document_hash body String Yes SHA-256 hash of your copy of the document. Accepts raw 64-char hex or a `sha256:` / `sha-256:` prefixed string.
Timing-safe comparison. Hash comparison uses constant-time comparison to prevent timing side-channel attacks. Both match and mismatch return 200 — check the match boolean.

Request body

{
  "document_hash": "sha256:a1b2c3d4e5f6..."
}

200 Response — match

{
  "match": true,
  "message": "Document matches the sealed original",
  "sealed_at": "2026-05-01T14:23:07Z",
  "verification_url": "https://verify.engineeringid.com/abc123def456"
}

200 Response — tampered / mismatch

{
  "match": false,
  "message": "Document does not match. The file may have been modified after sealing.",
  "sealed_at": "2026-05-01T14:23:07Z",
  "verification_url": "https://verify.engineeringid.com/abc123def456"
}

cURL example

curl -X POST https://api.engineeringid.com/v1/verify/abc123def456/match \
  -H "Content-Type: application/json" \
  -d '{"document_hash":"sha256:a1b2c3d4e5f6..."}'

GET /v1/verify/:code/stats

Returns aggregate verification statistics for a sealed document: how many times it has been verified and when it was sealed. Useful for embedding a "verified N times" badge. No sensitive document data is included. No authentication required.

GET /v1/verify/:code/stats
Auth
None
Returns
application/json
Rate limit
60 / min per IP

Parameters

Parameter In Type Required Description
code path String Yes 12-character lowercase alphanumeric verification code printed on the sealed document (e.g., "abc123def456").

200 Response

{
  "verification_code": "abc123def456",
  "verification_count": 14,
  "sealed_at": "2026-05-01T14:23:07Z"
}

cURL example

curl https://api.engineeringid.com/v1/verify/abc123def456/stats

POST /v1/verify/pdf

Verify a sealed PDF by combining its SHA-256 hash and embedded verification code in a single request. Used by the Chrome extension and Acrobat plugin, which compute the hash client-side and extract the code from the PDF metadata. No authentication required.

POST /v1/verify/pdf
Auth
None
Content-Type
application/json
Rate limit
60 / min per IP

Parameters

Parameter In Type Required Description
hash body String Yes SHA-256 hash of the PDF file. Raw 64-char hex or `sha256:`-prefixed form accepted.
code body String Yes 12-character lowercase alphanumeric verification code embedded in the PDF.
Status field, not HTTP status. All responses return 200. The status field is "valid", "tampered", or "not_found". Validation errors return 422.

Request body

{
  "hash": "a1b2c3d4e5f6...",
  "code": "abc123def456"
}

200 Response — valid

{
  "status": "valid",
  "seal": {
    "verification_code": "abc123def456",
    "sealed_at": "2026-05-01T14:23:07Z",
    "professional_name": "Jane Smith",
    "license_number": "PE-12345"
  }
}

200 Response — tampered

{
  "status": "tampered"
}

cURL example

curl -X POST https://api.engineeringid.com/v1/verify/pdf \
  -H "Content-Type: application/json" \
  -d '{"hash":"a1b2c3d4e5f6...","code":"abc123def456"}'

Rate limiting

All limits are per IP address. Exceeding the limit returns 429 Too Many Requests — implement exponential backoff and cache responses when possible.

Endpoint group Limit Window Reason
POST /v1/stamp 10 / min 60 s SVG-to-PNG/PDF rasterization invokes a Rust NIF — CPU-intensive
GET /v1/verify/:code
POST /v1/verify/:code/match
GET /v1/verify/:code/stats
POST /v1/verify/pdf
60 / min 60 s Prevents brute-force enumeration of verification codes

Error handling

Best practices

  • Always check the HTTP status code. Success is 200; errors are 400, 404, 429, or 500.
  • For non-200 responses, parse the JSON error object to get the error code and message.
  • Implement exponential backoff if you receive rate limit errors (429).
  • Cache responses when possible. The API returns Cache-Control: public, max-age=3600, so browser and CDN caching is encouraged.

Common issues

Issue Cause Solution
400 Missing required field name, license, or state is missing or empty Verify all required fields are present and non-empty strings
400 Unknown profession profession is not a recognized type Use one of: engineer, architect, land-surveyor, landscape-architect (or engineer subtypes)
400 Unknown format format is not svg, png, or pdf Use format=svg, format=png (default), or format=pdf
400 Invalid style override A style parameter (font_*, fill, stroke, etc.) was sent with a value that doesn't match its allowed pattern. Consult the parameter table for that field. Hex colors must be #RGB / #RGBA / #RRGGBB / #RRGGBBAA; sizes accept px / pt / em / rem / %; opacity values are decimals 0–1.
404 No stamp template Profession + state combination not supported Verify the state name. Some professions may not have templates in all states.
429 Rate limit exceeded More than 10 requests in 60 seconds from your IP Implement exponential backoff; cache results when possible
500 Render failed Unexpected error in PNG/PDF rasterization (SVG format avoids this) Try format=svg instead, or retry the request

Contact us

Developer Support

Get in touch

Tell us what you're building. We answer integration, schema, auth, rate-limit, SDK, and pricing questions — usually within a business day.

No spam, unsubscribe anytime. We respect your privacy.

Need help integrating?

Tell us about your use case and our developer team will respond with implementation details.