AI generations and jobs

Start an AI generation of memocards or questions (202 Accepted), follow the job with GET /v1/jobs, and read the AI credits it plans to use.

api.memojin.com/v1 · Contract 1.0.0-beta.2 · Beta

Sections · AI generations and jobs

Memojin’s AI generates memocards from the text of a content, then questions from its memocards. A generation is asynchronous: the API answers at once 202 Accepted with a job, which you follow with GET /v1/jobs/{jobId}.

How it runs

  1. Start the generation: 202, with jobId and the estimate of AI credits (estimate.units, Memojin-Credits-Estimate header).
  2. Poll the job about every 5 seconds: pending, then running (with progress from 0 to 100).
  3. On succeeded, list the content’s memocards or questions. On failed (error: "generation_failed") or cancelled, there is nothing to read; quote the job identifier when you write to us.

The full loop

import os, time, uuid, requests

API = "https://api.memojin.com/v1"
H = {"Authorization": f"Bearer {os.environ['MEMOJIN_API_KEY']}"}

# 1. Start the generation: 202 Accepted
job = requests.post(
    f"{API}/contents/{content_id}/memocard-generations",
    headers={**H, "Idempotency-Key": str(uuid.uuid4())},
    timeout=30,
)
job.raise_for_status()
job = job.json()  # {"jobId": ..., "status": "pending", "estimate": {"operation": "ai_card", "units": 12}}

# 2. Poll the job every 5 seconds until it is finished
while True:
    state = requests.get(f"{API}/jobs/{job['jobId']}", headers=H, timeout=10).json()
    if state["status"] in ("succeeded", "failed", "cancelled"):
        break
    time.sleep(5)

# 3. Read the memocards of the content
if state["status"] == "succeeded":
    cards = requests.get(f"{API}/contents/{content_id}/memocards", params={"limit": 100}, headers=H, timeout=30).json()["data"]

Refusals before any generation

  • 422 content_too_short: the content’s text is under 100 characters.
  • 422 nothing_to_generate: every part of the content already has its memocards.
  • 422 no_memocards: the content has no memocards to draw questions from.
  • 422 all_memocards_have_questions: every memocard already has its questions.
  • 402 insufficient_credits: the account has too few AI credits left.

In the sandbox

With a mj_test_ key, the generation is simulated: the job is succeeded at once, no memocard is created, no credit is used (see sandbox). Being told by webhook when a job ends Coming soon.

The GenerationAccepted object

A generation accepted for asynchronous processing.

FieldTypeDescription
jobIdalways presentstring (uuid)Job to follow with GET /v1/jobs/{jobId}.
statusalways present"pending" | "succeeded"pending when accepted; succeeded at once in the sandbox (mj_test_), where nothing is generated.
estimatealways presentobjectWhat the generation plans to use (also in the Memojin-Credits-Estimate header).
GenerationAccepted
{
  "jobId": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "status": "pending",
  "estimate": {
    "operation": "ai_card",
    "units": 12
  }
}

The Job object

An asynchronous job (an AI generation).

FieldTypeDescription
idalways presentstring (uuid)Job identifier (UUID).
typealways presentstringWhat the job does: memocard_generation, question_generation, or other. New values may appear.
statusalways presentstringpending, running, succeeded, failed or cancelled. Poll until succeeded, failed or cancelled.
progressalways presentintegerfrom 0 to 100Progress, 0 to 100.
contentIdalways presentstring | nullContent the job works on, or null.
erroralways presentstring | nullError code when the job failed (generation_failed), or null. Details stay internal: quote the job id when contacting us.
createdAtalways presentstring (date-time)Creation instant (ISO 8601, UTC).
updatedAtalways presentstring (date-time)Last update instant (ISO 8601, UTC).
Job
{
  "id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "type": "memocard_generation",
  "status": "running",
  "progress": 40,
  "contentId": "3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87",
  "error": null,
  "createdAt": "2026-10-04T10:00:00.000Z",
  "updatedAt": "2026-10-04T10:00:20.000Z"
}

Generate memocards from a content

POST/v1/contents/{contentId}/memocard-generationsScope generations:write

Generates memocards from the parts of the content body that have none yet. Asynchronous: answers 202 with a jobId to poll with GET /v1/jobs/{jobId}. Uses the AI credits of the account (one ai_card unit per memocard, see estimate.units and the Memojin-Credits-Estimate header); 402 insufficient_credits when they run out. Business refusals: 422 content_too_short, 422 nothing_to_generate. With a mj_test_ key, nothing is generated and no credit is used.

Parameters

FieldInTypeDescription
contentIdrequiredpathstring (uuid)Content identifier (UUID).
Idempotency-KeyoptionalheaderstringStrongly recommended. A unique value per operation (UUID v4), reused unchanged on every retry. Within 24 hours, the same key and the same request replay the first response (Idempotent-Replayed: true) without a second effect or charge; another request with the same key → 422 idempotency_key_reused; still running → 409 idempotency_in_progress.

Response 202

Generation accepted — A GenerationAccepted object

Errors

HTTPMeaning
400validation_error — contentId is not a UUID
401invalid_api_key or revoked_api_key
402insufficient_credits
403insufficient_scope, account_suspended, live_not_enabled or forbidden
404not_found
406not_acceptable — the Accept header excludes application/json
409idempotency_in_progress — retry after Retry-After (1 s)
422idempotency_key_reused, or a business rule (see the description)
429rate_limited or test_quota_exceeded — see the Retry-After and RateLimit-* headers
500internal_error
503unavailable — see the Retry-After header
Request
curl -X POST "https://api.memojin.com/v1/contents/3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87/memocard-generations" \
  -H "Authorization: Bearer $MEMOJIN_API_KEY" \
  -H "Idempotency-Key: 6f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87"
Response 202
{
  "jobId": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "status": "pending",
  "estimate": {
    "operation": "ai_card",
    "units": 12
  }
}

Generate questions from a content

POST/v1/contents/{contentId}/question-generationsScope generations:write

Generates practice questions for the memocards of the content that have none yet. Asynchronous: answers 202 with a jobId to poll with GET /v1/jobs/{jobId}. Uses the AI credits of the account (one ai_card unit per question, see estimate.units and the Memojin-Credits-Estimate header); 402 insufficient_credits when they run out. Business refusals: 422 no_memocards, 422 all_memocards_have_questions. With a mj_test_ key, nothing is generated and no credit is used.

Parameters

FieldInTypeDescription
contentIdrequiredpathstring (uuid)Content identifier (UUID).
Idempotency-KeyoptionalheaderstringStrongly recommended. A unique value per operation (UUID v4), reused unchanged on every retry. Within 24 hours, the same key and the same request replay the first response (Idempotent-Replayed: true) without a second effect or charge; another request with the same key → 422 idempotency_key_reused; still running → 409 idempotency_in_progress.

Response 202

Generation accepted — A GenerationAccepted object

Errors

HTTPMeaning
400validation_error — contentId is not a UUID
401invalid_api_key or revoked_api_key
402insufficient_credits
403insufficient_scope, account_suspended, live_not_enabled or forbidden
404not_found
406not_acceptable — the Accept header excludes application/json
409idempotency_in_progress — retry after Retry-After (1 s)
422idempotency_key_reused, or a business rule (see the description)
429rate_limited or test_quota_exceeded — see the Retry-After and RateLimit-* headers
500internal_error
503unavailable — see the Retry-After header
Request
curl -X POST "https://api.memojin.com/v1/contents/3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87/question-generations" \
  -H "Authorization: Bearer $MEMOJIN_API_KEY" \
  -H "Idempotency-Key: 6f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87"
Response 202
{
  "jobId": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "status": "pending",
  "estimate": {
    "operation": "ai_card",
    "units": 12
  }
}

Retrieve a job

GET/v1/jobs/{jobId}Scope jobs:read

Status and progress of an asynchronous generation. Poll it every few seconds (5 s is a good pace) until status is succeeded, failed or cancelled; then list the memocards or questions of the content.

Parameters

FieldInTypeDescription
jobIdrequiredpathstring (uuid)Job identifier (UUID), as returned by a generation.

Response 200

The job — A Job object

Errors

HTTPMeaning
400validation_error — an identifier is not a UUID
401invalid_api_key or revoked_api_key
403insufficient_scope, account_suspended, live_not_enabled or forbidden
404not_found
406not_acceptable — the Accept header excludes application/json
429rate_limited or test_quota_exceeded — see the Retry-After and RateLimit-* headers
500internal_error
503unavailable — see the Retry-After header
Request
curl "https://api.memojin.com/v1/jobs/9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d" \
  -H "Authorization: Bearer $MEMOJIN_API_KEY"
Response 200
{
  "id": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "type": "memocard_generation",
  "status": "running",
  "progress": 40,
  "contentId": "3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87",
  "error": null,
  "createdAt": "2026-10-04T10:00:00.000Z",
  "updatedAt": "2026-10-04T10:00:20.000Z"
}