Developer docs

Feedback Flows API

Mark work, check integrity, sync a roster, and pull results back into your LMS or MIS. Every call uses the same key; sandbox keys return mocks and never charge credits.

Base URL https://feedbackflows.org/api/v1 · Header X-API-Key

On this page

Getting started Try it Standalone testers
Account
GET /account/status GET /me GET /openapi.json GET /stats/users GET /test
Marking
POST /ai-tutor POST /interactive-feedback POST /smart-essay-marker POST /students/{student_id}/worksheet
Integrity
POST /ai-detector POST /full-reference-check POST /reference-check POST /style-comparison
Jobs
POST /ai-detector/jobs GET /ai-detector/jobs/{job_id} POST /ai-detector/jobs/{job_id}/cancel POST /ai-tutor/jobs GET /ai-tutor/jobs/{job_id} POST /ai-tutor/jobs/{job_id}/cancel POST /automarking/jobs GET /automarking/jobs/{job_id} POST /automarking/jobs/{job_id}/cancel POST /batch-style/jobs GET /batch-style/jobs/{job_id} POST /batch-style/jobs/{job_id}/cancel POST /exam-marker/jobs POST /exam-marker/jobs/{job_id}/cancel POST /interactive-feedback/jobs GET /interactive-feedback/jobs/{job_id} POST /interactive-feedback/jobs/{job_id}/cancel GET /jobs POST /jobs/estimate-cost POST /learner-packs/jobs GET /learner-packs/jobs/{job_id} POST /learner-packs/jobs/{job_id}/cancel POST /media-marker/jobs GET /media-marker/jobs/{job_id} POST /media-marker/jobs/{job_id}/cancel POST /reference-check/jobs GET /reference-check/jobs/{job_id} POST /reference-check/jobs/{job_id}/cancel POST /rubric-marking/jobs GET /rubric-marking/jobs/{job_id} POST /rubric-marking/jobs/{job_id}/cancel POST /smart-essay/jobs GET /smart-essay/jobs/{job_id} POST /smart-essay/jobs/{job_id}/cancel POST /worksheets/jobs GET /worksheets/jobs/{job_id} POST /worksheets/jobs/{job_id}/cancel
Exports
GET /automarking/jobs/{job_id}/export.csv GET /automarking/results/{result_id}/export.pdf GET /classes/{class_id}/export.csv GET /learner-packs/jobs/{job_id}/export.pdf GET /media-marker/jobs/{job_id}/export.pdf GET /reference-check/jobs/{job_id}/export.pdf GET /rubric-marking/jobs/{job_id}/export.csv GET /rubric-marking/results/{result_id}/export.pdf GET /smart-essay/jobs/{job_id}/export.pdf GET /worksheets/jobs/{job_id}/export.pdf
Data
GET /classes POST /classes PATCH /classes/{class_id} GET /classes/{class_id}/assignments GET /classes/{class_id}/assignments/{assignment_id}/results GET /classes/{class_id}/results PATCH /classes/{class_id}/students GET /question-templates GET /rubrics POST /rubrics GET /rubrics/{rubric_id} GET /students POST /students PATCH /students/{student_id} GET /students/{student_id}/ai-history GET /students/{student_id}/submissions POST /students/bulk POST /students:bulk
Analytics
POST /classes/{class_id}/assignments/{assignment_id}/analyze POST /students/{student_id}/progress-report GET /usage
Webhooks
POST /webhooks/test Webhooks Errors

Getting started

1
Create a key in Settings → API keys. Use a sandbox key while you explore.
2
Call GET /me to confirm scopes and remaining rate limits.
3
Estimate cost with POST /jobs/estimate-cost before you queue work.
4
Submit a job, then poll its status URL or supply a callback_url.

Send X-API-Key on every request. Default scopes are marking, integrity and analytics. Add media or admin when you need them. Sync calls are 60/minute; job submissions are 10/hour. Async POSTs accept an Idempotency-Key header.

cURL
curl https://feedbackflows.org/api/v1/me \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY"
Python
import os, requests
r = requests.get(
    "https://feedbackflows.org/api/v1/me",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
)
print(r.json())

Try it

Runs against this origin. Use a sandbox key so nothing is billed. The dropdown is built from the live OpenAPI spec.

Request console No proxy · same-origin fetch
GET
Idle
Headers

              
Response

              
cURL

            

Standalone testers

Quick testers (including the older “v2” URLs) still call /api/v1. Dedicated pages are useful for file uploads and polling.

Show tester pages

Endpoint reference

Generated from the live routing table. Search filters this list.

No endpoints match that search.

Account

GET /api/v1/account/status
/api/v1/account/status

Account status and remaining credits

Account status tester

GET /api/v1/me
/api/v1/me

Current API key, scopes and remaining rate limits

GET /api/v1/openapi.json
/api/v1/openapi.json

This document (no API key required)

GET /api/v1/stats/users
/api/v1/stats/users

Total user count (requires an admin-owned key)

Account status tester

Marking

POST /api/v1/ai-tutor
/api/v1/ai-tutor

Formative tutor feedback on a draft

scope: marking charges credits

Requires the `marking` scope.

Body fieldTypeRequired
student_draft string yes
mode string no
marking_rubric string no
persona string no

AI tutor

curl -X POST https://feedbackflows.org/api/v1/ai-tutor \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_draft": "example",
  "mode": "general",
  "marking_rubric": "example",
  "persona": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/ai-tutor",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_draft": "example",
  "mode": "general",
  "marking_rubric": "example",
  "persona": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/ai-tutor", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_draft": "example",
  "mode": "general",
  "marking_rubric": "example",
  "persona": "example"
}),
});
POST /api/v1/interactive-feedback
/api/v1/interactive-feedback

Inline feedback against a list of criteria

scope: marking charges credits

Requires the `marking` scope.

Body fieldTypeRequired
student_text string yes
marking_criteria array yes

Interactive feedback

curl -X POST https://feedbackflows.org/api/v1/interactive-feedback \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/interactive-feedback",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
},
)
await fetch("https://feedbackflows.org/api/v1/interactive-feedback", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
}),
});
POST /api/v1/smart-essay-marker
/api/v1/smart-essay-marker

Mark one essay against criteria (synchronous) (deprecated)

scope: marking charges credits

Requires the `marking` scope.

Body fieldTypeRequired
essay_text string yes
marking_criteria array yes

Smart essay marker

curl -X POST https://feedbackflows.org/api/v1/smart-essay-marker \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "essay_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/smart-essay-marker",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "essay_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
},
)
await fetch("https://feedbackflows.org/api/v1/smart-essay-marker", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "essay_text": "example",
  "marking_criteria": [
    "Clarity"
  ]
}),
});
POST /api/v1/students/{student_id}/worksheet
/api/v1/students/{student_id}/worksheet

Generate a targeted practice worksheet

scope: marking charges credits

Requires the `marking` scope.

Body fieldTypeRequired
weaknesses_list array no
format string no
level string no
num_questions integer no

Worksheets

curl -X POST https://feedbackflows.org/api/v1/students/{student_id}/worksheet \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example",
  "num_questions": 1
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/students/{student_id}/worksheet",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example",
  "num_questions": 1
},
)
await fetch("https://feedbackflows.org/api/v1/students/{student_id}/worksheet", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example",
  "num_questions": 1
}),
});

Integrity

POST /api/v1/ai-detector
/api/v1/ai-detector

Hybrid AI detector (synchronous, detailed response) (deprecated)

scope: integrity charges credits

Requires the `integrity` scope.

Body fieldTypeRequired
document_text string yes
academic_mode boolean no

AI detector and references

curl -X POST https://feedbackflows.org/api/v1/ai-detector \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_text": "example",
  "academic_mode": false
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/ai-detector",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "document_text": "example",
  "academic_mode": false
},
)
await fetch("https://feedbackflows.org/api/v1/ai-detector", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "document_text": "example",
  "academic_mode": false
}),
});
POST /api/v1/full-reference-check
/api/v1/full-reference-check

Bibliography and citation checker

scope: integrity charges credits

Requires the `integrity` scope.

Body fieldTypeRequired
document_text string yes
style string no
enable_semantic_check boolean no

AI detector and references

curl -X POST https://feedbackflows.org/api/v1/full-reference-check \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_text": "example",
  "style": "APA",
  "enable_semantic_check": false
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/full-reference-check",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "document_text": "example",
  "style": "APA",
  "enable_semantic_check": false
},
)
await fetch("https://feedbackflows.org/api/v1/full-reference-check", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "document_text": "example",
  "style": "APA",
  "enable_semantic_check": false
}),
});
POST /api/v1/reference-check
/api/v1/reference-check

Legacy AI detector (simplified response) (deprecated)

scope: integrity charges credits

Requires the `integrity` scope.

Body fieldTypeRequired
document_text string yes

AI detector and references

curl -X POST https://feedbackflows.org/api/v1/reference-check \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_text": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/reference-check",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "document_text": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/reference-check", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "document_text": "example"
}),
});
POST /api/v1/style-comparison
/api/v1/style-comparison

Compare one submission against a student's writing samples

scope: integrity charges credits

Requires the `integrity` scope.

Body fieldTypeRequired
student_id integer yes
submission_text string yes

Style comparison

curl -X POST https://feedbackflows.org/api/v1/style-comparison \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_id": 1,
  "submission_text": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/style-comparison",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_id": 1,
  "submission_text": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/style-comparison", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_id": 1,
  "submission_text": "example"
}),
});

Jobs

POST /api/v1/ai-detector/jobs
/api/v1/ai-detector/jobs

Queue AI detection (preferred over the synchronous endpoint)

scope: integrity scope: marking charges credits async job

Requires one of the following scopes: `integrity` or `marking`.

Body fieldTypeRequired
document_text string yes
academic_mode boolean no
callback_url string no
curl -X POST https://feedbackflows.org/api/v1/ai-detector/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_text": "example",
  "academic_mode": false,
  "callback_url": "https://example.com/webhooks/feedbackflows"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/ai-detector/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "document_text": "example",
  "academic_mode": false,
  "callback_url": "https://example.com/webhooks/feedbackflows"
},
)
await fetch("https://feedbackflows.org/api/v1/ai-detector/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "document_text": "example",
  "academic_mode": false,
  "callback_url": "https://example.com/webhooks/feedbackflows"
}),
});
GET /api/v1/ai-detector/jobs/{job_id}
/api/v1/ai-detector/jobs/{job_id}

AI detection job status and results

scope: integrity scope: marking

Requires one of the following scopes: `integrity` or `marking`.

POST /api/v1/ai-detector/jobs/{job_id}/cancel
/api/v1/ai-detector/jobs/{job_id}/cancel

Cancel an AI detector job and refund unused credits

scope: integrity scope: marking

Requires one of the following scopes: `integrity` or `marking`.

POST /api/v1/ai-tutor/jobs
/api/v1/ai-tutor/jobs

Queue AI tutor feedback

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
student_draft string yes
mode string no
marking_rubric string no
persona string no
callback_url string no

AI tutor jobs

curl -X POST https://feedbackflows.org/api/v1/ai-tutor/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_draft": "example",
  "mode": "example",
  "marking_rubric": "example",
  "persona": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/ai-tutor/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_draft": "example",
  "mode": "example",
  "marking_rubric": "example",
  "persona": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/ai-tutor/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_draft": "example",
  "mode": "example",
  "marking_rubric": "example",
  "persona": "example"
}),
});
GET /api/v1/ai-tutor/jobs/{job_id}
/api/v1/ai-tutor/jobs/{job_id}

AI tutor job status and result

scope: marking

Requires the `marking` scope.

POST /api/v1/ai-tutor/jobs/{job_id}/cancel
/api/v1/ai-tutor/jobs/{job_id}/cancel

Cancel an AI tutor job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/automarking/jobs
/api/v1/automarking/jobs

Queue question-by-question automarking

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
zip_base64 string yes
question_template_id integer no
questions array no
mark_mode string no
filename string no
callback_url string no
credit_check_only boolean no

Automarking

curl -X POST https://feedbackflows.org/api/v1/automarking/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "zip_base64": "UEsDBBQAAAAA",
  "question_template_id": 1,
  "questions": [],
  "mark_mode": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/automarking/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "zip_base64": "UEsDBBQAAAAA",
  "question_template_id": 1,
  "questions": [],
  "mark_mode": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/automarking/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "zip_base64": "UEsDBBQAAAAA",
  "question_template_id": 1,
  "questions": [],
  "mark_mode": "example"
}),
});
GET /api/v1/automarking/jobs/{job_id}
/api/v1/automarking/jobs/{job_id}

Automarking job status, progress and results

scope: marking

Requires the `marking` scope.

POST /api/v1/automarking/jobs/{job_id}/cancel
/api/v1/automarking/jobs/{job_id}/cancel

Cancel an automarking or exam-marker job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/batch-style/jobs
/api/v1/batch-style/jobs

Queue a batch style comparison

scope: integrity charges credits async job

Requires the `integrity` scope.

Body fieldTypeRequired
zip_base64 string yes
filename string no
callback_url string no
credit_check_only boolean no

Batch style

curl -X POST https://feedbackflows.org/api/v1/batch-style/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows",
  "credit_check_only": false
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/batch-style/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows",
  "credit_check_only": false
},
)
await fetch("https://feedbackflows.org/api/v1/batch-style/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows",
  "credit_check_only": false
}),
});
GET /api/v1/batch-style/jobs/{job_id}
/api/v1/batch-style/jobs/{job_id}

Batch style job status and comparisons

scope: integrity

Requires the `integrity` scope.

POST /api/v1/batch-style/jobs/{job_id}/cancel
/api/v1/batch-style/jobs/{job_id}/cancel

Cancel a batch style job

scope: integrity

Requires the `integrity` scope.

POST /api/v1/exam-marker/jobs
/api/v1/exam-marker/jobs

Queue exam script marking for one student

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
student_id integer yes
zip_base64 string yes
filename string no
callback_url string no

Exam and media

curl -X POST https://feedbackflows.org/api/v1/exam-marker/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/exam-marker/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows"
},
)
await fetch("https://feedbackflows.org/api/v1/exam-marker/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "callback_url": "https://example.com/webhooks/feedbackflows"
}),
});
POST /api/v1/exam-marker/jobs/{job_id}/cancel
/api/v1/exam-marker/jobs/{job_id}/cancel

Cancel an automarking or exam-marker job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/interactive-feedback/jobs
/api/v1/interactive-feedback/jobs

Queue interactive feedback

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
student_text string yes
marking_criteria array yes
callback_url string no

Interactive-feedback jobs

curl -X POST https://feedbackflows.org/api/v1/interactive-feedback/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ],
  "callback_url": "https://example.com/webhooks/feedbackflows"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/interactive-feedback/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ],
  "callback_url": "https://example.com/webhooks/feedbackflows"
},
)
await fetch("https://feedbackflows.org/api/v1/interactive-feedback/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_text": "example",
  "marking_criteria": [
    "Clarity"
  ],
  "callback_url": "https://example.com/webhooks/feedbackflows"
}),
});
GET /api/v1/interactive-feedback/jobs/{job_id}
/api/v1/interactive-feedback/jobs/{job_id}

Interactive-feedback job status and result

scope: marking

Requires the `marking` scope.

POST /api/v1/interactive-feedback/jobs/{job_id}/cancel
/api/v1/interactive-feedback/jobs/{job_id}/cancel

Cancel an interactive-feedback job and refund unused credits

scope: marking

Requires the `marking` scope.

GET /api/v1/jobs
/api/v1/jobs

List recent jobs for this account, filtered by the key's scopes

POST /api/v1/jobs/estimate-cost
/api/v1/jobs/estimate-cost

Estimate the credit cost of an async job

Body fieldTypeRequired
job_type string yes
file_count integer no
student_count integer no

Cost estimate

curl -X POST https://feedbackflows.org/api/v1/jobs/estimate-cost \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "job_type": "example",
  "file_count": 1,
  "student_count": 1
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/jobs/estimate-cost",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "job_type": "example",
  "file_count": 1,
  "student_count": 1
},
)
await fetch("https://feedbackflows.org/api/v1/jobs/estimate-cost", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "job_type": "example",
  "file_count": 1,
  "student_count": 1
}),
});
POST /api/v1/learner-packs/jobs
/api/v1/learner-packs/jobs

Queue a learner pack build

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
student_id integer yes
template_id integer no
selected_result_refs array no
generation_options object no
callback_url string no

Learner packs

curl -X POST https://feedbackflows.org/api/v1/learner-packs/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_id": 1,
  "template_id": 1,
  "selected_result_refs": [],
  "generation_options": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/learner-packs/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_id": 1,
  "template_id": 1,
  "selected_result_refs": [],
  "generation_options": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/learner-packs/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_id": 1,
  "template_id": 1,
  "selected_result_refs": [],
  "generation_options": "example"
}),
});
GET /api/v1/learner-packs/jobs/{job_id}
/api/v1/learner-packs/jobs/{job_id}

Learner pack job status and rendered document

scope: marking

Requires the `marking` scope.

POST /api/v1/learner-packs/jobs/{job_id}/cancel
/api/v1/learner-packs/jobs/{job_id}/cancel

Cancel a learner-pack job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/media-marker/jobs
/api/v1/media-marker/jobs

Queue video or audio marking

scope: media charges credits async job

Requires the `media` scope.

Body fieldTypeRequired
criteria string yes
media_base64 string yes
filename string no
education_level string no
persona string no
presentation_type string no
callback_url string no

Exam and media

curl -X POST https://feedbackflows.org/api/v1/media-marker/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "criteria": "example",
  "media_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "education_level": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/media-marker/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "criteria": "example",
  "media_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "education_level": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/media-marker/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "criteria": "example",
  "media_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "education_level": "example"
}),
});
GET /api/v1/media-marker/jobs/{job_id}
/api/v1/media-marker/jobs/{job_id}

Media marking job status and feedback

scope: media

Requires the `media` scope.

POST /api/v1/media-marker/jobs/{job_id}/cancel
/api/v1/media-marker/jobs/{job_id}/cancel

Cancel a media-marker job and refund unused credits

scope: media

Requires the `media` scope.

POST /api/v1/reference-check/jobs
/api/v1/reference-check/jobs

Queue the full reference-check web pipeline

scope: integrity charges credits async job

Requires the `integrity` scope.

Body fieldTypeRequired
document_text string yes
style string no
enable_semantic_check boolean no
match_refs boolean no
callback_url string no

Reference-check jobs

curl -X POST https://feedbackflows.org/api/v1/reference-check/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_text": "example",
  "style": "example",
  "enable_semantic_check": false,
  "match_refs": false
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/reference-check/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "document_text": "example",
  "style": "example",
  "enable_semantic_check": false,
  "match_refs": false
},
)
await fetch("https://feedbackflows.org/api/v1/reference-check/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "document_text": "example",
  "style": "example",
  "enable_semantic_check": false,
  "match_refs": false
}),
});
GET /api/v1/reference-check/jobs/{job_id}
/api/v1/reference-check/jobs/{job_id}

Reference-check job status and report

scope: integrity

Requires the `integrity` scope.

POST /api/v1/reference-check/jobs/{job_id}/cancel
/api/v1/reference-check/jobs/{job_id}/cancel

Cancel a reference-check job and refund unused credits

scope: integrity

Requires the `integrity` scope.

POST /api/v1/rubric-marking/jobs
/api/v1/rubric-marking/jobs

Queue rubric marking for a class ZIP

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
rubric_id integer yes
zip_base64 string yes
filename string no
marking_style string no
grading_system string no
callback_url string no
credit_check_only boolean no

Rubric marking

curl -X POST https://feedbackflows.org/api/v1/rubric-marking/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rubric_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "marking_style": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/rubric-marking/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "rubric_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "marking_style": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/rubric-marking/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "rubric_id": 1,
  "zip_base64": "UEsDBBQAAAAA",
  "filename": "example",
  "marking_style": "example"
}),
});
GET /api/v1/rubric-marking/jobs/{job_id}
/api/v1/rubric-marking/jobs/{job_id}

Rubric marking job status and results

scope: marking

Requires the `marking` scope.

POST /api/v1/rubric-marking/jobs/{job_id}/cancel
/api/v1/rubric-marking/jobs/{job_id}/cancel

Cancel a rubric marking job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/smart-essay/jobs
/api/v1/smart-essay/jobs

Queue essay marking (preferred over the synchronous endpoint)

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
essay_text string yes
marking_criteria string yes
education_level string no
persona string no
callback_url string no
curl -X POST https://feedbackflows.org/api/v1/smart-essay/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "essay_text": "example",
  "marking_criteria": "example",
  "education_level": "example",
  "persona": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/smart-essay/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "essay_text": "example",
  "marking_criteria": "example",
  "education_level": "example",
  "persona": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/smart-essay/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "essay_text": "example",
  "marking_criteria": "example",
  "education_level": "example",
  "persona": "example"
}),
});
GET /api/v1/smart-essay/jobs/{job_id}
/api/v1/smart-essay/jobs/{job_id}

Essay marking job status and feedback

scope: marking

Requires the `marking` scope.

POST /api/v1/smart-essay/jobs/{job_id}/cancel
/api/v1/smart-essay/jobs/{job_id}/cancel

Cancel a smart-essay job and refund unused credits

scope: marking

Requires the `marking` scope.

POST /api/v1/worksheets/jobs
/api/v1/worksheets/jobs

Queue worksheet generation

scope: marking charges credits async job

Requires the `marking` scope.

Body fieldTypeRequired
student_id integer yes
weaknesses_list array no
format string no
level string no
num_questions integer no
callback_url string no

Worksheet jobs

curl -X POST https://feedbackflows.org/api/v1/worksheets/jobs \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "student_id": 1,
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/worksheets/jobs",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "student_id": 1,
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/worksheets/jobs", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "student_id": 1,
  "weaknesses_list": [
    "Clarity"
  ],
  "format": "example",
  "level": "example"
}),
});
GET /api/v1/worksheets/jobs/{job_id}
/api/v1/worksheets/jobs/{job_id}

Worksheet job status and result

scope: marking

Requires the `marking` scope.

POST /api/v1/worksheets/jobs/{job_id}/cancel
/api/v1/worksheets/jobs/{job_id}/cancel

Cancel a worksheet job and refund unused credits

scope: marking

Requires the `marking` scope.

Exports

GET /api/v1/automarking/jobs/{job_id}/export.csv
/api/v1/automarking/jobs/{job_id}/export.csv

Export automarking results as CSV

scope: marking

Requires the `marking` scope.

GET /api/v1/automarking/results/{result_id}/export.pdf
/api/v1/automarking/results/{result_id}/export.pdf

Export one automarking result as PDF

scope: marking

Requires the `marking` scope.

GET /api/v1/classes/{class_id}/export.csv
/api/v1/classes/{class_id}/export.csv

Export a class roster as CSV

scope: analytics

Requires the `analytics` scope.

GET /api/v1/learner-packs/jobs/{job_id}/export.pdf
/api/v1/learner-packs/jobs/{job_id}/export.pdf

Export a learner pack as PDF

scope: marking

Requires the `marking` scope.

GET /api/v1/media-marker/jobs/{job_id}/export.pdf
/api/v1/media-marker/jobs/{job_id}/export.pdf

Export a media-marker report

scope: media

Requires the `media` scope.

GET /api/v1/reference-check/jobs/{job_id}/export.pdf
/api/v1/reference-check/jobs/{job_id}/export.pdf

Export a reference-check report as PDF

scope: integrity

Requires the `integrity` scope.

GET /api/v1/rubric-marking/jobs/{job_id}/export.csv
/api/v1/rubric-marking/jobs/{job_id}/export.csv

Export rubric batch results as CSV

scope: marking

Requires the `marking` scope.

Exports

GET /api/v1/rubric-marking/results/{result_id}/export.pdf
/api/v1/rubric-marking/results/{result_id}/export.pdf

Export one rubric result as PDF

scope: marking

Requires the `marking` scope.

GET /api/v1/smart-essay/jobs/{job_id}/export.pdf
/api/v1/smart-essay/jobs/{job_id}/export.pdf

Export a smart-essay report as PDF

scope: marking

Requires the `marking` scope.

GET /api/v1/worksheets/jobs/{job_id}/export.pdf
/api/v1/worksheets/jobs/{job_id}/export.pdf

Export a completed worksheet as PDF

scope: marking

Requires the `marking` scope.

Data

GET /api/v1/classes
/api/v1/classes

List classes

LMS roster sync

POST /api/v1/classes
/api/v1/classes

Create a class

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
name string yes
subject string no

LMS roster sync

curl -X POST https://feedbackflows.org/api/v1/classes \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example",
  "subject": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/classes",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "name": "example",
  "subject": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/classes", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "name": "example",
  "subject": "example"
}),
});
PATCH /api/v1/classes/{class_id}
/api/v1/classes/{class_id}

Update a class

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
name string no
subject string no
concluded boolean no
curl -X PATCH https://feedbackflows.org/api/v1/classes/{class_id} \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example",
  "subject": "example",
  "concluded": false
}'
import os, requests
requests.patch(
    "https://feedbackflows.org/api/v1/classes/{class_id}",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "name": "example",
  "subject": "example",
  "concluded": false
},
)
await fetch("https://feedbackflows.org/api/v1/classes/{class_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "name": "example",
  "subject": "example",
  "concluded": false
}),
});
GET /api/v1/classes/{class_id}/assignments
/api/v1/classes/{class_id}/assignments

List assignments for a class

GET /api/v1/classes/{class_id}/assignments/{assignment_id}/results
/api/v1/classes/{class_id}/assignments/{assignment_id}/results

Per-student outcomes for one assignment

scope: analytics

Requires the `analytics` scope.

Class assignment analysis

GET /api/v1/classes/{class_id}/results
/api/v1/classes/{class_id}/results

Bulk export marking outcomes for a class

scope: analytics

Requires the `analytics` scope.

PATCH /api/v1/classes/{class_id}/students
/api/v1/classes/{class_id}/students

Add or remove students from a class

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
add_student_ids array no
remove_student_ids array no
curl -X PATCH https://feedbackflows.org/api/v1/classes/{class_id}/students \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "add_student_ids": [],
  "remove_student_ids": []
}'
import os, requests
requests.patch(
    "https://feedbackflows.org/api/v1/classes/{class_id}/students",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "add_student_ids": [],
  "remove_student_ids": []
},
)
await fetch("https://feedbackflows.org/api/v1/classes/{class_id}/students", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "add_student_ids": [],
  "remove_student_ids": []
}),
});
GET /api/v1/question-templates
/api/v1/question-templates

List question templates

scope: marking

Requires the `marking` scope.

GET /api/v1/rubrics
/api/v1/rubrics

List rubrics

POST /api/v1/rubrics
/api/v1/rubrics

Create a rubric

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
name string yes
criteria string yes
marking_guidance string no
pass_rate integer no
max_attempts integer no
curl -X POST https://feedbackflows.org/api/v1/rubrics \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example",
  "criteria": "example",
  "marking_guidance": "example",
  "pass_rate": 1
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/rubrics",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "name": "example",
  "criteria": "example",
  "marking_guidance": "example",
  "pass_rate": 1
},
)
await fetch("https://feedbackflows.org/api/v1/rubrics", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "name": "example",
  "criteria": "example",
  "marking_guidance": "example",
  "pass_rate": 1
}),
});
GET /api/v1/rubrics/{rubric_id}
/api/v1/rubrics/{rubric_id}

Get one rubric with its criteria

GET /api/v1/students
/api/v1/students

List students

LMS roster sync

POST /api/v1/students
/api/v1/students

Create a student

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
name string yes
uln string no
class_ids array no

LMS roster sync

curl -X POST https://feedbackflows.org/api/v1/students \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example",
  "uln": "example",
  "class_ids": []
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/students",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "name": "example",
  "uln": "example",
  "class_ids": []
},
)
await fetch("https://feedbackflows.org/api/v1/students", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "name": "example",
  "uln": "example",
  "class_ids": []
}),
});
PATCH /api/v1/students/{student_id}
/api/v1/students/{student_id}

Update a student

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
name string no
uln string no
class_ids array no
curl -X PATCH https://feedbackflows.org/api/v1/students/{student_id} \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example",
  "uln": "example",
  "class_ids": []
}'
import os, requests
requests.patch(
    "https://feedbackflows.org/api/v1/students/{student_id}",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "name": "example",
  "uln": "example",
  "class_ids": []
},
)
await fetch("https://feedbackflows.org/api/v1/students/{student_id}", {
  method: "PATCH",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "name": "example",
  "uln": "example",
  "class_ids": []
}),
});
GET /api/v1/students/{student_id}/ai-history
/api/v1/students/{student_id}/ai-history

AI detection history for one student

Student analytics

GET /api/v1/students/{student_id}/submissions
/api/v1/students/{student_id}/submissions

Marking results and detector runs for one student

scope: analytics

Requires the `analytics` scope.

Student analytics

POST /api/v1/students/bulk
/api/v1/students/bulk

Create up to 200 students in one call (JSON array, {students: []}, or CSV)

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
students array no

LMS roster sync

curl -X POST https://feedbackflows.org/api/v1/students/bulk \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "students": []
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/students/bulk",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "students": []
},
)
await fetch("https://feedbackflows.org/api/v1/students/bulk", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "students": []
}),
});
POST /api/v1/students:bulk
/api/v1/students:bulk

Create up to 200 students in one call (JSON array, {students: []}, or CSV)

scope: marking

Requires the `marking` scope.

Body fieldTypeRequired
students array no

LMS roster sync

curl -X POST https://feedbackflows.org/api/v1/students:bulk \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "students": []
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/students:bulk",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "students": []
},
)
await fetch("https://feedbackflows.org/api/v1/students:bulk", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "students": []
}),
});

Analytics

POST /api/v1/classes/{class_id}/assignments/{assignment_id}/analyze
/api/v1/classes/{class_id}/assignments/{assignment_id}/analyze

Cohort analysis for one assignment

scope: analytics charges credits

Requires the `analytics` scope.

Class assignment analysis

POST /api/v1/students/{student_id}/progress-report
/api/v1/students/{student_id}/progress-report

Generate a progress report for one student

scope: analytics charges credits

Requires the `analytics` scope.

Body fieldTypeRequired
start_date string no
end_date string no

Student analytics

curl -X POST https://feedbackflows.org/api/v1/students/{student_id}/progress-report \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "start_date": "example",
  "end_date": "example"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/students/{student_id}/progress-report",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "start_date": "example",
  "end_date": "example"
},
)
await fetch("https://feedbackflows.org/api/v1/students/{student_id}/progress-report", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "start_date": "example",
  "end_date": "example"
}),
});
GET /api/v1/usage
/api/v1/usage

Usage and credit consumption for this account's API keys

scope: analytics

Requires the `analytics` scope.

Webhooks

POST /api/v1/webhooks/test
/api/v1/webhooks/test

Send a signed ping webhook to verify your receiver

Body fieldTypeRequired
callback_url string yes
curl -X POST https://feedbackflows.org/api/v1/webhooks/test \
  -H "X-API-Key: $FEEDBACKFLOWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "callback_url": "https://example.com/webhooks/feedbackflows"
}'
import os, requests
requests.post(
    "https://feedbackflows.org/api/v1/webhooks/test",
    headers={"X-API-Key": os.environ["FEEDBACKFLOWS_API_KEY"]},
    json={
  "callback_url": "https://example.com/webhooks/feedbackflows"
},
)
await fetch("https://feedbackflows.org/api/v1/webhooks/test", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.FEEDBACKFLOWS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "callback_url": "https://example.com/webhooks/feedbackflows"
}),
});

Webhooks

Job POSTs accept callback_url. Completions are signed with X-FeedbackFlows-Signature: t=<unix>,v1=<hex> over <timestamp>.<raw-body>. Also sent: X-FeedbackFlows-Delivery and X-FeedbackFlows-Timestamp. Reject timestamps older than five minutes. Use POST /webhooks/test to ping your receiver (5 per hour).

Verify a signature
import hmac, hashlib, time

def valid(header, body: bytes, secret: str, now=None) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    ts, sig = int(parts["t"]), parts["v1"]
    if abs((now or time.time()) - ts) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(sig, expected)

Errors

JSON bodies use error, code, status and request_id. Look for X-Request-Id on every response.

StatusCodeMeaning
400BAD_REQUESTMalformed JSON or missing fields
401UNAUTHORIZED / KEY_EXPIREDMissing, invalid, or expired key
402INSUFFICIENT_CREDITSNot enough credits
403SCOPE_FORBIDDENKey lacks the required scope
404NOT_FOUNDUnknown resource
429RATE_LIMITEDSee Retry-After
500INTERNAL_ERRORUnexpected failure