Overview
The EngineeringID GraphQL API gives you a single endpoint for everything in the platform — documents, credentials, seals, the professional directory, and more. Query exactly the fields you need, mutate state with strongly typed inputs, subscribe to live updates over WebSockets.
Endpoint
/graphql
- Base URL
https://api.engineeringid.com- Content-Type
application/json- Subscriptions
wss://api.engineeringid.com/socket
Smoke test
curl https://api.engineeringid.com/graphql \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "query { me { id email } }"
}'
Authentication
Every request must carry a bearer token in the Authorization
header — either a session cookie token (browser) or an API key (server-to-server).
Authorization: Bearer cred_live_a1b2c3d4...
- API keys are SHA-256 hashed before storage. The raw value is shown to you exactly once at creation time.
- Per-key scopes control which operations a key can perform — independent of user identity.
- Per-key rate limits isolate noisy integrations from each other.
Operations
The schema covers the full surface of the platform. Below are the most common operations developers reach for first. For an exhaustive list, query the introspection endpoint or open the schema browser.
Queries
Get the current user
query Me {
me {
id
email
name
organizations {
id
name
plan
}
}
}
List documents (paginated)
query Documents($first: Int!, $after: String) {
documentsConnection(first: $first, after: $after) {
edges {
cursor
node {
id
filename
contentType
size
insertedAt
sealedAt
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
Variables
{
"first": 25,
"after": null
}
Verify a seal by code
Public — does not require authentication. Useful for embedding verification widgets in third-party sites.
query VerifySeal($code: String!) {
verifySeal(code: $code) {
valid
verificationCode
sealedAt
document {
filename
version
documentHash
}
credential {
licenseNumber
stateCode
profession
verificationMethod
}
sealedBy {
name
}
}
}
Mutations
All mutations return a payload shaped { result, errors }. Validation errors are reported in
errors[]
with a field, message, and machine-readable code
— top-level errors
are reserved for transport / auth failures.
Seal a document
mutation CreateSeal($input: CreateSealInput!) {
createSeal(input: $input) {
seal {
id
verificationCode
sealedAt
document {
id
version
}
}
errors {
field
message
code
}
}
}
Variables
{
"input": {
"documentId": "01HXYZ-doc-uuid",
"credentialId": "01HXYZ-cred-uuid"
}
}
File uploads
File uploads use the
GraphQL multipart request spec. Use any compliant client — apollo-upload-client, graphql-request, etc.
// Multipart request — see graphql-multipart-request-spec
// Uses a custom GraphQLUpload plug that extracts uploads into context.
mutation UploadDocument($file: Upload!, $folderId: ID) {
createDocument(file: $file, folderId: $folderId) {
document {
id
filename
size
}
errors {
field
message
}
}
}
GraphQLUpload
plug — this avoids Blueprint.Input.parse
crashes that older approaches hit.
Subscriptions
Subscriptions stream over Phoenix Channels at wss://api.engineeringid.com/socket. Authenticate the same way as queries — pass the bearer token in the connect payload.
subscription BatchSealJobUpdates($jobId: ID!) {
batchSealJobUpdated(jobId: $jobId) {
id
status
total
processed
succeeded
failed
}
}
Pagination
All list queries follow the
Relay connection spec: edges { cursor node }
+ pageInfo { hasNextPage endCursor }. Use first
/ after
for forward paging, last/before for backward.
pageInfo
for the actual page size you got back.
Errors
GraphQL responses always return HTTP 200
when the request was understood. Application errors live in the response body, not the status code.
{
"errors": [
{
"message": "Document not found",
"extensions": {
"code": "NOT_FOUND",
"field": "documentId"
},
"path": ["createSeal"]
}
],
"data": {
"createSeal": null
}
}
Error codes
| Code | Meaning | What to do |
|---|---|---|
UNAUTHENTICATED |
Missing or invalid auth token | Provide a valid API key or session token via Authorization header |
FORBIDDEN |
Authenticated but not allowed to perform this action | Check the user's permissions in the target organization |
NOT_FOUND |
Resource doesn't exist or the user can't see it | Verify IDs and the active organization context |
VALIDATION_FAILED |
Input failed schema validation | Inspect the per-field errors[] for details |
RATE_LIMITED |
Too many requests within the rate window | Back off exponentially and retry |
INTERNAL_ERROR |
Unexpected server-side failure | Retry; if it persists, contact developer support |
Rate limits
RATE_LIMITED
in the error extensions. Back off exponentially and respect the retry-after
header.
Need a deeper schema explorer?
The full schema (every type, field, and argument) is available via introspection or our hosted GraphiQL playground. Reach out and we'll point you to the right environment.
Contact us
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.