Skip to main content
Work through this page before your first real exam. Each section ends with what to check.
There is no sandbox or test mode. Every key is live, every graded copy spends real credits, and exams you create appear in the institute’s dashboard. Test with short exams and a handful of copies, and delete test exams you no longer need.

1. Keys per environment

API keys belong to an institute. An admin of that institute creates them in the Vacademy admin dashboard under Settings → Integrations → API keys (the card for Evaluation API keys), once Evalezy has enabled the Evaluation API for the institute (hello@evalezy.com).
  • One key per environment and per system. Separate keys for staging and production, and for each system that calls the API (for example the ERP backend and a nightly sync job). You can then revoke one without stopping the others, and tell their traffic apart.
  • Give each key only the scopes it needs. New keys get evaluation:read and evaluation:write.
Keep evaluation:finalize on the one system that publishes results.
  • Set an expiry for keys used in pilots or by contractors.
  • Check each key with GET /me. It needs no scope and returns who the key is and what it can do:
Response
Production and staging use different keys, each with the fewest scopes it needs, and GET /me returns the institute you expect.

2. Store keys as secrets

A key is vak_eval_ followed by 48 hexadecimal characters, and it is shown once, when it is created.
  • Server-to-server only. Never put a key in a web page, a mobile app or any code that runs on a user’s device. Your app calls your backend; your backend calls Evalezy.
  • Keep it in a secret manager or an environment variable injected at deploy time, never in source control.
  • Never log the full key. Log the first 16 characters if you need to tell keys apart.
  • Rotate without downtime. Create the new key, deploy it, confirm traffic with GET /me, then revoke the old key. Revocation takes effect within about a minute.
  • Revoke at once if a key may have leaked, then create a new one.
The key lives only in your server-side secret store, appears in no logs or client code, and you have rotated it once in staging.

3. Retries with Idempotency-Key

Networks fail. Every POST accepts an optional Idempotency-Key header, so a retried request never creates a second exam, submission or charge.
  • Derive the key from your own IDs, for example sub-{exam_id}-{student_id}-{upload_id}, so a retry after a crash reuses it. Any printable ASCII string up to 255 characters works.
  • Same key, same request: you get the stored response again, with the header Idempotent-Replayed: true.
  • Same key, different request: 422 idempotency_key_reused. This is a bug in how you build keys.
  • Same key while the first request is still running: 409 request_in_progress. Wait a moment and retry with the same key.
  • Keys are kept for 48 hours, per institute, so a retry from a second key of the same institute replays too.
Successful responses and most 4xx errors are stored and replayed. After fixing the cause of one of those 4xx errors, retry with a new key. 402, 409, 429 and 5xx responses are not stored, so retry those with the same key: after topping up credits, the same Idempotency-Key runs the request again.
The API also guards against duplicates on its own:
Every POST your integration makes sends an Idempotency-Key built from your own IDs, and every exam has an external_ref.

4. Error handling

Every error has the same shape, and every response carries an X-Request-Id header:
Branch on error.code, never on message. Codes never change once published; messages may. Log request_id with every failure. A grading failure is not an HTTP error. The submission was accepted, and later its status became failed with an error.code such as copy_unreadable, language_not_supported or timed_out. Handle these in your sync worker. Successful responses can also carry warnings[], for example auto_rubric, negative_marks_ignored or pages_beyond_vision_limit. Log them and show the important ones to the person who set up the exam.

Text you send

  • All text is plain text, never rendered as HTML. Send text, not markup, and render what you read back as text.
  • Titles, question and option labels, section names, criterion names and candidate names, roll numbers and classes cannot contain < or >. Such a value returns 422 validation_failed with the field code invalid_characters.
  • A NUL character (\u0000) anywhere in a request body returns 422 validation_failed with the field code invalid_characters. Strip it from text copied out of other systems.
  • metadata is a JSON object of up to 2 KB.
Your client branches on error.code, logs request_id, retries only 409 request_in_progress, 429 and any 5xx (parsing error bodies defensively), and surfaces failed submissions to a person.

5. Credits

Grading is paid with credits from the institute’s balance. Prices are fixed and known before grading: Institutes on a contract price see "rate_source": "contract" in quotes. Credits are bought in the Vacademy admin dashboard; current packs are on evalezy.com/pricing. Monitor the balance from your side:
  • GET /credits returns balance, committed (quoted for copies still in the queue), available (what new submissions can use), the institute’s rate_card and spent_30d.
  • POST /credits/quote prices a batch before you submit it. Send exactly one of pages, upload_ids (exact page counts of uploads, up to 100) or typed_answers. The answer has total, available and sufficient.
  • A submission that can’t be paid for returns 402 insufficient_credits with required and available in details. Nothing is queued.
Alert the institute’s admin when available drops below a few days of typical use, and quote large batches (a whole term’s copies) before you start uploading.
Your integration checks GET /credits on a schedule, alerts below a threshold, and quotes big batches first.

6. Quotas and rate limits

Daily copy quota. Each institute can submit a fixed number of copies per UTC day, 2,000 by default; re-evaluations count too. A key can also have its own lower daily cap (daily_copy_cap in GET /me). Past the limit, submissions return 429 daily_quota_exceeded with details.quota, details.limit, details.resets_at and a Retry-After header. Quotas reset at 00:00 UTC. For exam seasons or large programmes, ask hello@evalezy.com for a higher quota well before the date. Rate limits. Requests are limited per key and per institute: GET requests, POST /exams/search, POST /candidates/search and POST /credits/quote count as reads; other requests count as writes. POST /uploads also counts each file presigned. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A refused request returns 429 rate_limited with Retry-After. Size limits to design around:
Your uploads and submissions are paced under the write limits, your client honours Retry-After, and your quota covers your busiest day.

7. The teacher review step

The AI never publishes results. Every mark is a draft until you finalize it, and nothing is sent to students or parents when you do; publishing is up to you.
  • Decide who reviews. Teachers can review on the institute’s Vacademy dashboard, where API exams are tagged Source: API, or in your own UI through PATCH /submissions/{id}/questions/{question_id} and POST /submissions/{id}/approve.
  • Review flagged copies first. needs_review is true when a question failed, the AI’s confidence was low, or a copy had more than 40 pages. Filter with needs_review=true.
  • Finalize deliberately. POST /exams/{id}/finalize locks results: overrides and re-evaluation then return 409 submission_finalized. Partially graded and failed copies are skipped unless you send allow_partial.
  • Plan for rechecks. POST /submissions/{id}/unfinalize with a reason puts one result back on hold; correct it, then finalize again.
Your product makes clear which marks are AI drafts, gives teachers a place to review them, and only publishes finalized results.

8. Know what is not available yet

Check that your launch doesn’t depend on something on the Roadmap: webhooks (poll instead, see Syncing results), phone photos (send one PDF), bulk batches matched by name, creating an exam from a question-paper PDF, rubric generation on request and rubric locking, hosted review links, CSV results, Hindi and regional languages, and SDKs. Choice groups (“attempt any N of M”) are in beta and enabled on request.

Support

Email hello@evalezy.com. For a problem with a specific request, include the request_id from the error (or the X-Request-Id header), the endpoint, and the time in UTC. Never send your API key.

Handwritten term exams

The full school flow, end to end.

Syncing results

The polling worker your launch needs.