SnapReport
POST/api/v1/snapreport/analyze

Send auditor evidence (notes, transcripts, audio, images, PDFs) against a standard. SnapReport AI returns a structured list of grading suggestions, one per relevant clause. Stateless — no audit is persisted on our side.

Authentication

Requires snapreport:generate permission. See Authentication.

Request — JSON — text only

curl -X POST https://api.myqateam.com/api/v1/snapreport/analyze \
-H "Authorization: Bearer myqa_..." \
-H "Content-Type: application/json" \
-d '{
"standard_id": "<uuid>",
"language": "en",
"evidence": [
{ "type": "text", "content": "Observed cold-chain logs, no breaches." }
]
}'

Request — multipart — files

To include audio, images, PDFs, DOCX, or XLSX/XLS, send as multipart/form-data. Scalar fields are plain form fields; processes is a JSON-stringified array; notes is a JSON-stringified array of strings. Any form field whose value is a file is treated as binary evidence — field names don't matter.

curl -X POST https://api.myqateam.com/api/v1/snapreport/analyze \
-H "Authorization: Bearer myqa_..." \
-F 'standard_id=<uuid>' \
-F 'language=en' \
-F 'processes=["<chapter-id>","<chapter-id>"]' \
-F 'notes=["Observed cold-chain logs for Q1."]' \
-F 'file_1=@interview.mp3' \
-F 'file_2=@fridge-temp-log.jpg' \
-F 'file_3=@haccp-plan.pdf'

Remote files — Google Cloud Storage

Instead of uploading bytes, point us at a file you already hold in Google Cloud Storage. This is the recommended path for large audio: the recording is read directly from storage and never travels through the API request.

  • gcs_uri — a gs://bucket/object URI. One-time setup: grant the Storage Object Viewer role on your bucket to the service account we provide, and tell us the bucket name so it can be registered against your API key. Buckets that are not registered are refused.
  • url — a signed https://storage.googleapis.com/... URL. No IAM setup and no bucket registration: the signature in the URL is your own authorisation. Make sure it is still valid when you call us; we do not retry expired links.

Field names do not matter. Exactly as with file uploads, any field whose value is a gs:// URI or a Cloud Storage URL is treated as evidence — so file_1=@recording.mp3 and file_1=gs://your-bucket/recording.mp3 both work. Reserved fields (standard_id, language, processes, callback_url, callback_secret, notes, transcript) are never read as evidence.

The file type is detected from the file's actual content, not its extension or the type you declare. Declaring a type that disagrees with the bytes is rejected.

# gs:// reference — recommended for large audio
curl -X POST https://api.myqateam.com/api/v1/snapreport/analyze \
-H "Authorization: Bearer myqa_..." \
-H "Content-Type: application/json" \
-d '{
"standard_id": "<uuid>",
"language": "en",
"evidence": [
{ "type": "audio", "gcs_uri": "gs://your-bucket/interviews/site-visit.mp3" }
]
}'
# Signed URL — no IAM setup required
curl -X POST https://api.myqateam.com/api/v1/snapreport/analyze \
-H "Authorization: Bearer myqa_..." \
-H "Content-Type: application/json" \
-d '{
"standard_id": "<uuid>",
"evidence": [
{ "type": "audio", "url": "https://storage.googleapis.com/your-bucket/site-visit.mp3?X-Goog-Signature=..." }
]
}'

Fields

FieldTypeRequiredDescription
standard_iduuidYesWhich standard to grade against. List available IDs via GET /api/v1/standards.
processesuuid[]NoChapter IDs to scope grading to a subset of the standard. Discover IDs via GET /api/v1/standards/:id/clauses (use chapters[].id). Omit to scope to every chapter.
languageenumNoen, fr, de. Defaults to en.
evidencearrayYes (JSON)Array of evidence items. Inline: { type, content } with type text, transcript or structured. Remote: { type, gcs_uri } or { type, url }.
notesJSON stringNo (multipart)Inline text notes for multipart requests.
File fieldsFileNo (multipart)Any file field is treated as evidence. Type is inferred from MIME.
gcs_uristringNoSingle-file shorthand for a gs://bucket/object reference. The bucket must be registered against your API key.
urlstringNoSingle-file shorthand for a signed Cloud Storage URL. Must be HTTPS, on storage.googleapis.com, and carry an X-Goog-Signature.
callback_urlstringNoHTTPS webhook. Switches the call to async mode. Must be HTTPS and must not target localhost / private (RFC1918) / link-local IPs, or cloud-metadata services.
callback_secretstringNoHMAC-SHA256 secret for webhook signing.

File size limits

Limits are identical whether a file is uploaded or referenced.

  • Images: 20 MB each. JPEG, PNG, WebP, GIF.
  • PDFs: 20 MB each.
  • DOCX / XLSX / XLS: 20 MB each. Text is extracted server-side (charts and embedded images are dropped) and clipped at ~500K characters. Legacy .doc is not supported — save as .docx first.
  • Audio: 200 MB each. MP3, WAV, AAC, FLAC, WebM, M4A, MP4, OGG. For anything large, prefer gcs_uri — an upload of this size in a single request is far more likely to fail in transit.

Errors specific to remote files

FieldStatusDescription
FORBIDDEN403The bucket is not registered against your API key.
REMOTE_ACCESS_DENIED403We could not read the object. Check the IAM grant, or whether the signed URL has expired.
REMOTE_NOT_FOUND404The object does not exist at that path.
FILE_TOO_LARGE400The object exceeds the limit for its type.
REMOTE_FETCH_FAILED502Cloud Storage could not be reached or returned an unexpected error.

Response — synchronous

json
{
"success": true,
"data": {
"standard": { "id": "...", "code": "ISO 9001", "name": "ISO 9001:2015" },
"language": "en",
"suggestions": [
{
"clause_id": "uuid",
"clause_code": "4.1",
"is_knockout": false,
"grade": "A",
"comment": "Context analysis is reviewed annually; documented record from 2026-Q1 aligns with the quality policy.",
"reasoning": "Note states 'context analysis reviewed annually — no deviations observed'."
}
],
"validation_status": "pending_validation",
"requires_human_validation": true,
"notes_processed": 2,
"clauses_scanned": 46,
"processing_time_ms": 38211,
"token_usage": { "input_tokens": 14200, "output_tokens": 3800 }
}
}

Response — async acknowledgement

json
{
"success": true,
"data": {
"job_id": "...",
"status": "processing",
"estimated_duration_sec": 60
}
}

Webhook payload

When the job completes we send an HTTP POST to your callback_url with Content-Type: application/json. The request body is the JSON object shown below — the same data object you would have received synchronously, wrapped with job_id and success. If callback_secret was supplied, the request carries an X-Webhook-Signature header containing an HMAC-SHA256 hex digest of the raw body. Deliveries are best-effort and not retried — every attempt is recorded with response status, duration, and any error.

We do not authenticate the call to your endpoint with a Bearer token. Either verify the X-Webhook-Signature header using callback_secret, or include a hard-to-guess token in the URL itself (e.g. https://example.com/webhooks/snapreport/<random-token>).

Successful job payload

json
{
"job_id": "…",
"success": true,
"data": {
"standard": { "id": "…", "code": "ISO 9001", "name": "ISO 9001:2015" },
"language": "en",
"suggestions": [ /* same shape as the sync response */ ],
"validation_status": "pending_validation",
"requires_human_validation": true,
"notes_processed": 2,
"clauses_scanned": 46,
"processing_time_ms": 38211,
"token_usage": { "input_tokens": 14200, "output_tokens": 3800 }
}
}

Failed job payload

json
{
"job_id": "…",
"success": false,
"error": { "code": "PROCESSING_ERROR", "message": "…" }
}

Your endpoint should respond with 2xx within ~10 seconds; non-2xx responses are recorded but not retried.

Errors

See the full error code reference on the authentication page.

API Documentation — myQA.team