{
  "openapi": "3.1.0",
  "info": {
    "title": "CRED API",
    "version": "1.0.0-pilot",
    "description": "The Course Repository & Equivalency Database (CRED) API provides programmatic access to national course and transfer equivalency data. Core data is available as a public good; write access requires contributor authorization.\n\nDesign notes:\n- Institutions are keyed by IPEDS unit IDs.\n- Course records carry CIP 2020 classification codes (CEDS-aligned).\n- The course/section model aligns with the 1EdTech Edu-API specification; equivalency records extend it with provenance and versioning.\n- Records are versioned immutably; consumers receive the current version by default.",
    "contact": { "name": "CRED Developer Support", "email": "developers@cred.demo" },
    "license": { "name": "Data: CC BY 4.0 (core equivalency data)", "identifier": "CC-BY-4.0" }
  },
  "servers": [
    { "url": "/api/v1", "description": "This environment" }
  ],
  "security": [{ "apiKey": [] }, { "oauth2": [] }],
  "tags": [
    { "name": "Institutions", "description": "Participating institutions, keyed by IPEDS ID" },
    { "name": "Courses", "description": "Normalized course records" },
    { "name": "Equivalencies", "description": "Versioned transfer equivalency decisions with provenance" },
    { "name": "Datasets", "description": "Published dataset versions from contributing organizations" },
    { "name": "Webhooks", "description": "Event subscriptions with signed deliveries" },
    { "name": "Exports", "description": "Bulk data export" },
    { "name": "Keys", "description": "API key management" }
  ],
  "paths": {
    "/institutions": {
      "get": {
        "tags": ["Institutions"],
        "summary": "List institutions",
        "parameters": [
          { "name": "state", "in": "query", "schema": { "type": "string" }, "example": "NC" },
          { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["community_college", "four_year_public", "four_year_private"] } },
          { "name": "source", "in": "query", "schema": { "type": "string", "enum": ["acadeum", "ad_astra", "direct"] } },
          { "name": "q", "in": "query", "description": "Free-text search on name/city", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" }
        ],
        "responses": {
          "200": {
            "description": "Paginated institutions",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InstitutionList" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/institutions/{id}": {
      "get": {
        "tags": ["Institutions"],
        "summary": "Get an institution",
        "parameters": [{ "$ref": "#/components/parameters/id" }],
        "responses": {
          "200": { "description": "Institution", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Institution" } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/courses": {
      "get": {
        "tags": ["Courses"],
        "summary": "List courses",
        "parameters": [
          { "name": "institution", "in": "query", "schema": { "type": "string" }, "example": "inst_199856" },
          { "name": "cip", "in": "query", "description": "CIP 2020 code filter", "schema": { "type": "string" }, "example": "23.1301" },
          { "name": "q", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" }
        ],
        "responses": {
          "200": { "description": "Paginated courses", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseList" } } } }
        }
      },
      "post": {
        "tags": ["Courses"],
        "summary": "Bulk upsert courses",
        "description": "Accepts a single course object or an array of up to 500. Re-upserting an existing course increments its version and emits `course.updated`.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/CourseUpsert" }, { "type": "array", "items": { "$ref": "#/components/schemas/CourseUpsert" }, "maxItems": 500 } ] } } }
        },
        "responses": {
          "201": { "description": "Upsert results" },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/courses/{id}": {
      "get": {
        "tags": ["Courses"],
        "summary": "Get a course",
        "parameters": [{ "$ref": "#/components/parameters/id" }],
        "responses": {
          "200": { "description": "Course with institution expansion" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/equivalencies": {
      "get": {
        "tags": ["Equivalencies"],
        "summary": "List equivalencies",
        "parameters": [
          { "name": "sourceCourse", "in": "query", "schema": { "type": "string" } },
          { "name": "targetInstitution", "in": "query", "schema": { "type": "string" } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["accepted", "pending", "conflict"] } },
          { "name": "provenance", "in": "query", "schema": { "type": "string", "enum": ["acadeum", "ad_astra", "direct"] } },
          { "name": "expand", "in": "query", "description": "Set to `course` to embed source course and institution records", "schema": { "type": "string", "enum": ["course"] } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" }
        ],
        "responses": {
          "200": { "description": "Paginated equivalencies", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EquivalencyList" } } } }
        }
      },
      "post": {
        "tags": ["Equivalencies"],
        "summary": "Create an equivalency",
        "description": "Creates a new equivalency record (v1) and emits `equivalency.created` to subscribed webhooks.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EquivalencyCreate" } } }
        },
        "responses": {
          "201": { "description": "Created equivalency", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/Equivalency" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    },
    "/equivalencies/{id}": {
      "get": {
        "tags": ["Equivalencies"],
        "summary": "Get an equivalency",
        "parameters": [{ "$ref": "#/components/parameters/id" }],
        "responses": {
          "200": { "description": "Equivalency with course and institution expansion" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/equivalencies/{id}/versions": {
      "get": {
        "tags": ["Equivalencies"],
        "summary": "Get version history",
        "description": "Immutable snapshots of every prior state of the record, with change notes and actors.",
        "parameters": [{ "$ref": "#/components/parameters/id" }],
        "responses": {
          "200": { "description": "Current record plus historical snapshots" }
        }
      }
    },
    "/datasets": {
      "get": { "tags": ["Datasets"], "summary": "List dataset versions", "responses": { "200": { "description": "Dataset versions" } } },
      "post": {
        "tags": ["Datasets"],
        "summary": "Publish a dataset version",
        "description": "Records a published dataset version and emits `dataset.published`.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["label"], "properties": { "label": { "type": "string" }, "recordCount": { "type": "integer" }, "metadata": { "type": "object" } } } } } },
        "responses": { "201": { "description": "Published dataset version" } }
      }
    },
    "/datasets/{id}": {
      "get": {
        "tags": ["Datasets"],
        "summary": "Get a dataset version with validation issues",
        "parameters": [{ "$ref": "#/components/parameters/id" }],
        "responses": { "200": { "description": "Dataset version + row-level validation issues" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/webhooks": {
      "get": { "tags": ["Webhooks"], "summary": "List webhook endpoints", "responses": { "200": { "description": "Endpoints (secrets truncated)" } } },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register a webhook endpoint",
        "description": "The signing secret is returned exactly once, at creation.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url", "events"], "properties": { "url": { "type": "string", "examples": ["https://example.edu/hooks/cred"] }, "events": { "type": "array", "items": { "type": "string", "enum": ["equivalency.created", "equivalency.updated", "course.updated", "dataset.published"] } } } } } } },
        "responses": { "201": { "description": "Endpoint with one-time secret" } }
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": { "tags": ["Webhooks"], "summary": "List recent deliveries", "parameters": [{ "$ref": "#/components/parameters/id" }], "responses": { "200": { "description": "Delivery log with payloads, signatures, and response codes" } } }
    },
    "/webhooks/{id}/test": {
      "post": { "tags": ["Webhooks"], "summary": "Send a test event", "parameters": [{ "$ref": "#/components/parameters/id" }], "responses": { "200": { "description": "Delivery result" } } }
    },
    "/exports": {
      "get": {
        "tags": ["Exports"],
        "summary": "Bulk export",
        "parameters": [
          { "name": "resource", "in": "query", "schema": { "type": "string", "enum": ["equivalencies", "courses", "institutions"], "default": "equivalencies" } },
          { "name": "format", "in": "query", "schema": { "type": "string", "enum": ["csv", "json"], "default": "csv" } }
        ],
        "responses": { "200": { "description": "CSV file or JSON array" } }
      }
    },
    "/keys": {
      "get": { "tags": ["Keys"], "summary": "List API keys", "responses": { "200": { "description": "Keys (prefixes only)" } } },
      "post": { "tags": ["Keys"], "summary": "Create an API key", "description": "The full key is returned exactly once.", "responses": { "201": { "description": "Key with one-time plaintext" } } }
    },
    "/keys/{id}": {
      "delete": { "tags": ["Keys"], "summary": "Revoke an API key", "parameters": [{ "$ref": "#/components/parameters/id" }], "responses": { "204": { "description": "Revoked" } } }
    }
  },
  "webhooks": {
    "equivalency.created": {
      "post": {
        "summary": "New equivalency published",
        "description": "Sent when a new equivalency record is published. Verify the `X-CRED-Signature` header (HMAC-SHA256 over `{timestamp}.{body}` with your endpoint secret).",
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Event" }, "example": { "id": "evt_8f3kz1", "type": "equivalency.created", "created": "2026-05-01T15:04:05Z", "data": { "equivalencyId": "eq_0042", "status": "accepted" } } } } },
        "responses": { "200": { "description": "Acknowledge with any 2xx within 5 seconds" } }
      }
    },
    "equivalency.updated": {
      "post": { "summary": "Equivalency changed (new version recorded)", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Event" } } } }, "responses": { "200": { "description": "Acknowledged" } } }
    },
    "course.updated": {
      "post": { "summary": "Course record changed", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Event" } } } }, "responses": { "200": { "description": "Acknowledged" } } }
    },
    "dataset.published": {
      "post": { "summary": "Dataset version published", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Event" } } } }, "responses": { "200": { "description": "Acknowledged" } } }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "http", "scheme": "bearer", "bearerFormat": "cred_{env}_{token}", "description": "API key issued via the dashboard or /keys endpoint." },
      "oauth2": { "type": "oauth2", "flows": { "clientCredentials": { "tokenUrl": "https://auth.cred.demo/oauth/token", "scopes": { "read:public": "Read public course & equivalency data", "write:contribute": "Publish data as an authorized contributor" } } }, "description": "OAuth 2.0 client credentials for machine-to-machine integrations (production)." }
    },
    "parameters": {
      "id": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
      "limit": { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 25, "maximum": 100 } },
      "offset": { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } }
    },
    "responses": {
      "Unauthorized": { "description": "Missing or invalid API key (RFC 7807)", "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } } },
      "NotFound": { "description": "Resource not found (RFC 7807)", "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } } },
      "BadRequest": { "description": "Validation failure (RFC 7807)", "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } } }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "properties": { "type": { "type": "string" }, "title": { "type": "string" }, "status": { "type": "integer" }, "detail": { "type": "string" } }
      },
      "Pagination": {
        "type": "object",
        "properties": { "total": { "type": "integer" }, "limit": { "type": "integer" }, "offset": { "type": "integer" }, "returned": { "type": "integer" } }
      },
      "Institution": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "examples": ["inst_199856"] },
          "ipedsId": { "type": "string", "description": "IPEDS unit ID — the canonical institution key", "examples": ["199856"] },
          "name": { "type": "string", "examples": ["Wake Technical Community College"] },
          "city": { "type": "string" },
          "state": { "type": "string" },
          "type": { "type": "string", "enum": ["community_college", "four_year_public", "four_year_private"] },
          "system": { "type": ["string", "null"], "examples": ["North Carolina Community College System"] },
          "source": { "type": "string", "enum": ["acadeum", "ad_astra", "direct"], "description": "Onboarding channel for this institution's data" }
        }
      },
      "InstitutionList": {
        "type": "object",
        "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Institution" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }
      },
      "Course": {
        "type": "object",
        "description": "Aligned with the 1EdTech Edu-API course model; CIP codes per CEDS.",
        "properties": {
          "id": { "type": "string", "examples": ["crs_199856_eng111"] },
          "institutionId": { "type": "string" },
          "code": { "type": "string", "examples": ["ENG 111"] },
          "title": { "type": "string", "examples": ["Writing and Inquiry"] },
          "credits": { "type": "number", "examples": [3] },
          "level": { "type": "string", "enum": ["lower", "upper"] },
          "description": { "type": ["string", "null"] },
          "cipCode": { "type": ["string", "null"], "examples": ["23.1301"] },
          "version": { "type": "integer" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "CourseUpsert": {
        "type": "object",
        "required": ["institutionId", "code", "title", "credits", "level"],
        "properties": {
          "institutionId": { "type": "string" },
          "code": { "type": "string" },
          "title": { "type": "string" },
          "credits": { "type": "number" },
          "level": { "type": "string", "enum": ["lower", "upper"] },
          "description": { "type": "string" },
          "cipCode": { "type": "string", "pattern": "^\\d{2}\\.\\d{4}$" }
        }
      },
      "CourseList": {
        "type": "object",
        "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Course" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }
      },
      "Equivalency": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "examples": ["eq_0042"] },
          "sourceCourseId": { "type": "string" },
          "targetInstitutionId": { "type": "string" },
          "targetCourseId": { "type": ["string", "null"] },
          "targetCode": { "type": ["string", "null"], "examples": ["ENGL 101"] },
          "status": { "type": "string", "enum": ["accepted", "pending", "conflict"] },
          "decidedBy": { "type": ["string", "null"], "examples": ["Transfer Credit Committee"] },
          "decidedAt": { "type": ["string", "null"], "format": "date-time" },
          "policyBasis": { "type": ["string", "null"], "examples": ["NC Comprehensive Articulation Agreement"] },
          "version": { "type": "integer", "description": "Increments on every change; prior versions retained immutably" },
          "provenance": { "type": ["string", "null"], "enum": ["acadeum", "ad_astra", "direct", null] },
          "datasetVersionId": { "type": ["string", "null"] },
          "createdAt": { "type": "string", "format": "date-time" }
        }
      },
      "EquivalencyCreate": {
        "type": "object",
        "required": ["sourceCourseId", "targetInstitutionId"],
        "properties": {
          "sourceCourseId": { "type": "string" },
          "targetInstitutionId": { "type": "string" },
          "targetCourseId": { "type": "string" },
          "targetCode": { "type": "string" },
          "status": { "type": "string", "enum": ["accepted", "pending", "conflict"], "default": "pending" },
          "decidedBy": { "type": "string" },
          "policyBasis": { "type": "string" }
        }
      },
      "EquivalencyList": {
        "type": "object",
        "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Equivalency" } }, "pagination": { "$ref": "#/components/schemas/Pagination" } }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "examples": ["evt_8f3kz1"] },
          "type": { "type": "string", "enum": ["equivalency.created", "equivalency.updated", "course.updated", "dataset.published"] },
          "created": { "type": "string", "format": "date-time" },
          "data": { "type": "object" }
        }
      }
    }
  }
}
