Documentation pages

Upload & start a review

Use this flow for every file, up to 20 MiB. First prepare the upload, send the file, then start its review.

For Word documents with embedded images, export to PDF so the images are included in the review. Older .doc files are not supported; save them as .docx or PDF first.

1. Prepare the upload

POST/uploads

Key permission: reviews:write

Send JSON. Your API key authorizes the request; the returned URL authorizes only this file upload.

What to send

Only the filename is required. Steletta checks the uploaded file, measures its size and computes its checksum.

filenamestringRequired
Original filename ending in .pdf or .docx. Maximum 255 characters; no directory paths.
purposestring · optional
Free-form review context. Include labels such as Title:, Description:, Learning goals:, Audience:, Standards: or any other relevant information. Steletta reads all supplied context; there is no fixed list of labels or required format. Leave it out or blank to review the material as provided. Maximum 5,000 characters.
gradestringOptional
Optional grade or age group, for example Grade 6. Maximum 50 characters.
retainReportboolean · default trueOptional
Keep the report under your organization's retention policy. Set false for a temporary result available for 24 hours after completion.

Title and description are not separate request fields. Put them inside purpose, along with any other context you want considered.

Optional retry protection

Idempotency-Keystring · optional
Omit to create a new upload automatically. For safe retries, supply 8–255 visible ASCII characters and reuse that value for the same file and metadata. See safe retries.

What you get back

idstring
The upload ID. Use it in /uploads/{uploadId}/complete after sending the file.
uploadUrlstring
The authorized URL to send this one file to. Do not use it to retrieve results.
uploadHeadersobject
Headers to include when sending the file. Use the returned values unchanged.
expiresAttimestamp
The deadline to upload the file and submit it. This does not limit how long the review can run.

If a retry recovers an already submitted review, the response contains id and reviewId instead. Skip uploading and check that review's status.

What to send · POST /uploads
curl --fail-with-body -X POST 'https://www.steletta.com/api/v1/uploads' \
  -H "Authorization: Bearer $STELETTA_API_KEY" \
  -o upload.json \
  -H "Content-Type: application/json" \
  -d '{
  "filename": "assessment.pdf"
}'
Example response · Upload details (URL varies)
{
  "id": "upload_example",
  "uploadUrl": "https://storage.example/upload?signature=SHORT_LIVED_SIGNATURE",
  "uploadHeaders": {
    "Content-Type": "application/pdf"
  },
  "expiresAt": "2026-09-29T14:10:00Z"
}

2. Send the file

What to send

The file itself, as raw bytes in an HTTP PUT to the returned uploadUrl, with the returned uploadHeaders. There is no JSON body and no Steletta API-key header in this request.

What you get back

A successful 2xx response confirms the upload. You do not need its response body. Keep the upload ID from step 1 and use it to start the review.

Use the URL exactly as supplied, before expiresAt. Treat it as private. It permits only this upload, not reading, listing, deleting or overwriting files.

What to send · PUT to the returned uploadUrl
# upload.json is the response from POST /uploads.
curl --fail-with-body -X PUT "$(jq -r '.uploadUrl' upload.json)" \
  -H "Content-Type: $(jq -r '.uploadHeaders["Content-Type"]' upload.json)" \
  --data-binary @assessment.pdf

3. Start the review

POST/uploads/{uploadId}/complete

Key permission: reviews:write

What to send

The upload ID from step 1 in the URL, plus your API-key header. No request body or extra settings are needed.

What you get back

Confirmation that your review was accepted for processing, containing review and replayed. This is not the finished report. Save review.id: this is the review ID used to check status and retrieve the result. review.status is the initial job status—not the final verdict.

A successful retry returns the same review and replayed: true. It does not start a second review.

If completion fails temporarily, retry completion using the saved upload ID—do not upload the file again. If prepare returns a reviewId, skip uploading and go straight to that review's status.

The 10-minute window covers uploading and completing submission, not review processing. If the upload response is lost, try completion first; the server validates the uploaded file. A submitted review can still be retrieved after this window. An unsubmitted upload returning 410 upload_expired must be prepared again with a new idempotency key and uploaded again.

Request example
curl --fail-with-body -X POST 'https://www.steletta.com/api/v1/uploads/upload_example/complete' \
  -H "Authorization: Bearer $STELETTA_API_KEY"
Example response · Review started (excerpt)
{
  "review": {
    "id": "rev_example",
    "status": "queued",
    "createdAt": "2026-09-29T14:00:00Z",
    "completedAt": null,
    "usage": {
      "estimatedUnitsMilli": 1000,
      "finalUnitsMilli": null
    },
    "error": null
  },
  "replayed": false
}

Include your own context

Send purpose as one text value in the prepare request. Title, description, learning goals and any other labels are welcome; there is no required template.

Steletta interprets the supplied text as review context, not as separate API fields or instructions that override the review.

Existing integrations may still send contentType, size and sha256. If supplied, those values are checked against the uploaded file. They are not needed for a new integration.

Request with optional context
curl --fail-with-body -X POST 'https://www.steletta.com/api/v1/uploads' \
  -H "Authorization: Bearer $STELETTA_API_KEY" \
  -o upload.json \
  -H "Content-Type: application/json" \
  -d '{
  "filename": "assessment.pdf",
  "purpose": "Title: Grade 6 Ratios Practice\nDescription: Word problems and tables with an answer key\nLearning goals: Ratios and rates",
  "grade": "Grade 6"
}'