memorarecredentials
Back

API documentation

How to create, view and edit credentials and templates from your own software, and how to hear about changes. Everything here is live. Base address: https://credentials.memorare.ai

Authorization

Each organisation has issuing keys (Developers › Authorization Tokens, created by a platform admin). Send the key as Authorization: Bearer cred_.... A key works only from the server addresses listed on it, has its own rate limit (see the X-RateLimit-* headers) and is stored only as a fingerprint. Requests with a body use Content-Type: application/json. Every answer is JSON: {"ok": true, ...} or {"ok": false, "error": "..."} with the matching HTTP status.

Scopes: credentials:read to view, credentials:issue to create and edit (credentials and templates), credentials:revoke to revoke.

GET https://credentials.memorare.ai/api/v1/me          // who this key belongs to, its scopes and allowed addresses

Credentials: create, view, edit

Create. Issue from a published template by its id, or describe the achievement yourself.

POST https://credentials.memorare.ai/api/v1/credentials
{ "recipient_email": "ada@example.org",
  "template_id": "2a840cc6-b074-4a62-a1dd-d03332f0c231",
  "valid_until": "2028-01-31T00:00:00Z" }          // valid_until is optional

// or, without a template:
{ "recipient_email": "ada@example.org",
  "achievement": { "slug": "investing-101", "name": "Investing 101", "description": "...", "criteria": "..." } }

201 { "ok": true, "credential": { "id": "...", "url": "...", "verify_url": "https://credentials.memorare.ai/verify/...", "status": "valid", "jwt": "..." } }

With achievement, if a template with that slug exists the name, description and criteria must match it, and it must be published.

View.

GET https://credentials.memorare.ai/api/v1/credentials?status=valid&achievement=investing-101&recipient_email=ada@example.org&limit=25&cursor=...
GET https://credentials.memorare.ai/api/v1/credentials/:id          // one credential: status, accepted_at, the signed copy and the full document

Edit. A credential is signed, so what it says cannot be rewritten. What you can change is how long it stays valid. It is signed again under the same id, so links and the check page keep working.

PATCH https://credentials.memorare.ai/api/v1/credentials/:id
{ "valid_until": "2029-06-30T00:00:00Z" }        // or null for no expiry

Any other field is refused. To change the earner or the achievement, revoke the credential and issue a new one. A revoked credential cannot be edited.

Revoke.

POST https://credentials.memorare.ai/api/v1/credentials/:id/revoke
{ "reason": "Issued by mistake" }

Check any credential (no key needed): POST /api/v1/validate with {"id": "..."} or {"credential": "<compact JWS>"}.

Templates: create, view, edit

A template is the design of a credential. These are the same fields as the Create Template screen.

POST https://credentials.memorare.ai/api/v1/templates
{ "name": "Investing 101: Making Your Money Work for You",
  "description": "Completed Investing 101 on Money Smart Academy.",           // 10-500 characters
  "type": "Course",              // Badge, Certificate, CertificateOfCompletion, Certification, Course, Competency, MicroCredential, Award, Membership, License
  "level": "Foundational",       // Foundational, Intermediate, Advanced, Expert
  "time": "Weeks",               // optional: Hours, Days, Weeks, Months, Years
  "cost": "Free",                // optional: Free, Paid
  "skills": ["Investing", "Compound interest", "Risk management"],           // at least 3 to publish
  "criteria": [ { "type": "Course completion", "description": "Complete every lesson.", "url": "https://..." } ],
  "learn_more_url": "https://...",
  "earn_button": { "enabled": true, "url": "https://..." },
  "attributes_public": true,
  "signatories": [ { "name": "Kurt Russel", "designation": "Program Director" } ],
  "image": "data:image/png;base64,...",         // optional square PNG, up to 2048 pixels
  "state": "draft" }                            // draft (default) or published

201 { "ok": true, "template": { "id": "...", "state": "draft", ... } }

GET   https://credentials.memorare.ai/api/v1/templates?state=published     // your templates
GET   https://credentials.memorare.ai/api/v1/templates/:id                 // one template, with how many were issued and accepted
PATCH https://credentials.memorare.ai/api/v1/templates/:id                 // send only the fields to change
POST  https://credentials.memorare.ai/api/v1/templates/:id/publish         // draft to published
POST  https://credentials.memorare.ai/api/v1/templates/:id/archive         // stop issuing; credentials already issued keep working
POST  https://credentials.memorare.ai/api/v1/templates/:id/restore         // archived back to published

Once a credential has been issued from a template, its name, description, criteria, type and skills are fixed (they are written inside the signed credentials); a PATCH that changes them answers 409. Level, time, cost, links, the earn button, signatures and the picture can still change.

Webhooks

Add an address on the Webhooks tab. We send a POST with JSON when a credential is issued, accepted, edited or revoked. Events: credential.issued, credential.accepted, credential.updated, credential.revoked.

{ "id": "event id", "type": "credential.accepted", "created_at": "2026-10-13T09:00:00Z",
  "data": { "credential_id": "...", "verify_url": "https://credentials.memorare.ai/verify/...", "template_id": "...", "template_name": "...",
            "issued_at": "...", "valid_until": null, "accepted_at": "...", "revoked_at": null } }

Headers: x-creds-event, x-creds-delivery, x-creds-timestamp and x-creds-signature. The signature is sha256= and the HMAC-SHA256, with your signing secret, of timestamp + "." + body. Reject messages older than a few minutes. Answer with a 2xx status; ten failures in a row turn the webhook off. Addresses must be public https. Messages never contain an email address.

Set up a paid school automatically (platform key)

For apps that host other organisations, such as ed. When someone pays, the app's server sets up their school with one call: the school, its people and its own bearer key. No person is involved at any point, and nothing is copied by hand: the answer to the call contains the school's key.

The credentials involved

  • Platform key (the master key), plat_...: one per app, locked to the app's server addresses. It creates schools, adds their people, and creates and revokes their keys. It does not act inside a school (GET /api/v1/templates with it answers 401), and it sees only the schools that same app created. It stays on the app's server and is never given to an agent.
  • School bearer key, cred_...: one per school (or one per agent), made by the call below. It works only inside that school: templates, issuing, viewing, editing, revoking. This is the key you give an agent.
  • School id, your own, for example order-1042: names the school in every call (letters, digits and . _ : -, up to 80). Use the order or school id your app already has.

Step 1. Your server makes the call (every call is under https://credentials.memorare.ai/api/v1/platform, with Authorization: Bearer plat_...)

PUT https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/provision
{ "name": "Money Smart Academy",
  "url": "https://moneysmart.ed.memorare.ai",
  "owner_email": "owner@school.org",                      // or "members": [{ "email": "...", "role": "member" | "viewer" }] (up to 20)
  "key": { "name": "ed", "allowed_ips": ["158.101.117.93"] } }   // optional: "scopes" (credentials:issue, credentials:read, credentials:revoke), "per_minute"

201 { "ok": true, "created": true,
      "tenant": { "id": "{school-id}", "name": "...", "issuer_id": "...", "issuer_slug": "..." },
      "members": [{ "email": "owner@school.org", "role": "member", "created": true }],
      "key": { "id": 7, "secret": "cred_...", "prefix": "cred_xxxx", "name": "ed", "allowed_ips": [...], "scopes": [...], "created": true } }

Step 2. Save what came back with the school's record: the school id, key.secret and key.id. The secret is returned once; only a hash is stored here.

Step 3. Give the credentials to the agent. Tell it: use https://credentials.memorare.ai with Authorization: Bearer cred_... to work on this school. It can call the credentials and templates API above and nothing else: it cannot see another school, create schools or make keys.

  • Server addresses. A school key works only from the addresses in key.allowed_ips (1 to 10). If the agent runs on a different server than your app, list the agent's address there, or make the calls for the agent. From any other address the answer is 403.
  • Give an agent only what it needs. Set key.scopes: for example ["credentials:read"] for an agent that only looks things up, or add credentials:issue to let it issue. Add credentials:revoke only if it must revoke.
  • One key per agent. Use a different key.name for each (for example ed and agent-support): one call per name gives each its own key and limits, and each can be revoked on its own.
  • Taking access away. DELETE /tenants/{school-id}/keys/{id} with the platform key stops that key at once.

Safe to repeat. A retried payment changes nothing: the school is updated, the people are not duplicated, and a key is made only once per name. A repeat answers 200 with the key's details and "secret": null. If the secret was lost, send the same call with "rotate": true: the new key is made first and the old one revoked after, so there is no moment without a working key. Bad input answers 400 and creates nothing.

The owner signs in at https://credentials.memorare.ai with their memorare account and lands on their school. No password is ever sent.

Disabling a school (never deleted). A school is never removed, because every credential it issued must keep verifying. To stop it from being used, disable it: its keys answer 403, its people lose access, and nothing new can be issued or changed. Everything already issued keeps working: check pages, the validate API, the credential documents, the issuer page and the revocation list. Disabling is safe to repeat, and enabling undoes it.

POST   https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/disable    { "reason": "Subscription ended" }     // optional reason (3-300 characters)
DELETE https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}            // the same as disable: nothing is deleted
POST   https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/enable
200 { "ok": true, "tenant": { "id": "{school-id}", "status": "disabled", "disabled_at": "...", "disabled_reason": "Subscription ended", ... } }

Every answer about a school includes "status": "active" or "disabled". While a school is disabled its key answers 403 ("This school is disabled"), and changing it (the setup call, people, new keys) answers 409. Reading it and revoking its keys still work. Enabling it makes the setup call work again; a key revoked in between is replaced by a new one.

The same thing in separate calls

PUT    https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}                {"name": "...", "url": "https://..."}      // the school
POST   https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/members        {"email": "...", "role": "member"}        // a person
POST   https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/keys           {"name": "ed", "allowed_ips": ["..."]}    // a new key each time
GET    https://credentials.memorare.ai/api/v1/platform/me                                 // who this platform key is, its scopes and addresses
GET    https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}                // one school
GET    https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/keys           // its keys, never the secrets
DELETE https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/keys/{id}      // revoke a key
GET    https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/members
DELETE https://credentials.memorare.ai/api/v1/platform/tenants/{school-id}/members/{id}

Errors and limits. JSON with the usual status: 400 bad input (nothing is created), 401 no or wrong key, 403 wrong address or missing scope (the provision call needs tenants:write and keys:manage, plus members:write when people are sent), 404 not your school, 415 the body must be JSON, 429 too many requests (each key has a per-minute limit, see X-RateLimit-*), 1000 schools per app, 200 people per school.

Open Badges 3.0

Every credential is a signed Open Badges 3.0 credential. Learners can connect any wallet through the standard API described at /ims/ob/v3p0/discovery. The check page is https://credentials.memorare.ai/verify/<id>, the signing keys are at /.well-known/jwks.json, and revocation is published as a status list.