Eformogi Family API

The family side of the issuer rail: the courses a school declared for a claimed learner, and folding them into the family-issued Witness Transcript.

GitHub OpenAPI

Operations 2

GET /api/declared-courses The courses an issuer declared for the learners this family claimed #
POST /api/link-declared-courses Fold declared courses into the transcript the family issued #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/eformogi-com:eformogi-com-family-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

eformogi-com-family-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Eformogi Family API
  version: 33.5.1
  description: 'Eformogi is a record of learning the student owns, witnessed by the adults who were there, verifiable at one link with no login, and issued by the schools and counselors who hold the learners. The family-issued receipt (`tr_`) is a Witness Transcript. This document describes the machine-readable surface: verification, witness attestation, consent, the agent protocol, and the issuer rail (enrollment, roster, declared courses, and the family side that folds them into the transcript). The open record format itself is published at https://eformogi.com/spec.'
  contact:
    url: https://eformogi.com
  license:
    name: Proprietary
servers:
- url: https://eformogi.com
  description: Production
tags:
- name: Family
  description: 'The family side of the issuer rail: the courses a school declared for a claimed learner, and folding them into the family-issued Witness Transcript.'
paths:
  /api/declared-courses:
    get:
      summary: The courses an issuer declared for the learners this family claimed
      description: 'Lists the `cr_` rows an issuer declared for every learner bound to the caller (institutional_received_profiles.family_user_id, written only by the record claim at /claim/). Alumni and archived learners are included: a graduated learner''s record stays with the family. Withdrawn (revoked) courses are omitted. The user is taken from the verified session and from nowhere else; no query or body parameter is read. No email address ever appears in the response; the only identity published is the issuing org''s display name. Capped at 500 rows. A schema that predates docs/sql-migrations/2026-09-07-issuer-rail.sql answers an empty list, never an error. Rate limits: 60 requests per minute per IP and 30 per minute per user.'
      tags:
      - Family
      security:
      - bearerAuth: []
      responses:
        '200':
          description: 'Body: `courses` (array of { cr_receipt_id, profile_id, person_name, org_name (string or null), course_title, term, grade, credits (number or null), co_signed (number, hidden signatures excluded), tr_receipt_id (the transcript it is folded into, or null) }).'
        '401':
          description: 'Missing, invalid, or expired session. Body: `error`.'
        '429':
          description: 'Rate limited (60 per minute per IP, 30 per minute per user). Body: `error`, `retryAfterMs`.'
        '500':
          description: 'The read failed. Body: `error`.'
      operationId: getApiDeclaredCourses
      x-operation-id-source: derived
  /api/link-declared-courses:
    post:
      summary: Fold declared courses into the transcript the family issued
      description: 'Sets course_receipts.tr_receipt_id on each named `cr_` so /api/verify-receipt folds the declared course, with the teacher co-signatures it already carries, into the Witness Transcript''s per-course rollup. Nothing is re-minted and no hash changes; tr_receipt_id is provenance outside the `cr_` hash domain. Four checks against the verified session: the `tr_` exists and its issuer_user is the caller (a guest-issued transcript has no provable owner and is refused); every `cr_` is bound to the caller through a claimed learner, else the whole request is a 403 and a security event is logged; the `cr_` names the same learner as the transcript (case and spacing insensitive, otherwise exact), so one sibling''s course never lands on another''s transcript; a `cr_` already on a transcript the caller does not own is skipped, never re-pointed. The write re-asserts the binding filter so it cannot outlive the check. Idempotent: re-sending the same body links nothing new and answers 200. Cap: 200 course receipts per call. Rate limits: 60 requests per minute per IP and 30 per minute per user.'
      tags:
      - Family
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - tr_receipt_id
              - cr_receipt_ids
              properties:
                tr_receipt_id:
                  type: string
                  pattern: ^tr_[a-fA-F0-9]{12}$
                  description: The family-issued transcript. Must be owned by the caller.
                cr_receipt_ids:
                  type: array
                  minItems: 1
                  maxItems: 200
                  items:
                    type: string
                    pattern: ^cr_[a-fA-F0-9]{12}$
                  description: Declared course receipts from /api/declared-courses. Duplicates are collapsed.
      responses:
        '200':
          description: 'Body: `ok` (true), `linked` (number of rows updated), `skipped` (array of { cr_receipt_id, reason: `withdrawn` | `different_learner` | `already_linked` | `linked_elsewhere` }).'
        '400':
          description: '`tr_receipt_id` is not `tr_` plus 12 hex characters; `cr_receipt_ids` is not an array, is empty, has more than 200 entries, or contains a malformed id. Body: `error`.'
        '401':
          description: 'Missing, invalid, or expired session. Body: `error`.'
        '403':
          description: 'The caller does not own the transcript, or at least one `cr_` is not bound to the caller (also the answer on a schema that predates the issuer-rail migration, where no binding can be proved). The whole request is refused. Body: `error`.'
        '404':
          description: 'Unknown `tr_receipt_id`. Body: `error`.'
        '429':
          description: 'Rate limited (60 per minute per IP, 30 per minute per user). Body: `error`, `retryAfterMs`.'
        '500':
          description: 'A lookup or the link write failed. Body: `error`.'
      operationId: postApiLinkDeclaredCourses
      x-operation-id-source: derived
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Supabase JWT access token
    orgApiKey:
      type: apiKey
      in: header
      name: Authorization
      description: Institutional org API key. Use "Bearer efg_org_..." or "Api-Key efg_org_..." header. Keys are scoped to specific data categories.