> ## Documentation Index
> Fetch the complete documentation index at: https://thehyperstack.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Open Badges

> Read your credentials as Open Badges 2.0 and 3.0, and get your organization's issuer DID.

Every Hyperstack credential is also available as an Open Badge. Hyperstack serves two versions of the 1EdTech Open Badges standard:

* **Open Badges 2.0**: a hosted assertion, a badge class, an issuer profile and a baked badge image.
* **Open Badges 3.0**: a W3C Verifiable Credential, signed with your organization's key, and a DID document for your organization.

Other platforms can read these URLs to import or check your credentials.

## Before you start

* These URLs are public. They do not need an API key.
* Use `GET`. `POST` also works.
* Any website can read them from the browser (CORS is open).
* They are on `https://api.thehyperstack.com`. Open Badges 2.0 URLs start with `/v1/openbadge/`. Open Badges 3.0 and DID URLs start with `/openbadge/v3/`.

In the URLs on this page, `{key}` is the key of one credential. It is the same value as `document_id` in API responses and [webhook events](/docs/api-guide/webhook-events). It is also the last part of the credential page URL:

```text theme={null}
https://hyperstack.id/credential/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f
```

The key must be a UUID, such as `9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f`.

Only published credentials have Open Badges. A credential that is a draft or under review returns `404`. Revoked and suspended credentials still return data, so a platform can see their status. See [Revoked and suspended credentials](#revoked-and-suspended-credentials).

## All URLs

| URL | Returns |
| - | - |
| `/v1/openbadge/{key}/badge.json` | Open Badges 2.0 assertion |
| `/v1/openbadge/{key}/badgeclass.json` | Open Badges 2.0 badge class |
| `/v1/openbadge/{key}/issuer.json` | Open Badges 2.0 issuer profile |
| `/v1/openbadge/{key}/badge.png` | Badge image, baked with the 2.0 assertion URL |
| `/openbadge/v3/{key}/credential.json` | Open Badges 3.0 credential, signed |
| `/openbadge/v3/{key}` | Open Badges 3.0 credential, not signed |
| `/openbadge/v3/{key}/achievement.json` | Open Badges 3.0 achievement |
| `/openbadge/v3/{key}/issuer.json` | Open Badges 3.0 issuer profile |
| `/openbadge/v3/{key}/revocation_list.json` | Open Badges 3.0 revocation list |
| `/openbadge/v3/{key}/badge.png` | Badge image, baked with the signed 3.0 credential |
| `/openbadge/v3/issuers/{issuer_id}/did.json` | DID document of your organization |
| `/openbadge/v3/issuers/{issuer_id}` | The same DID document |

## Open Badges 2.0

The 2.0 files use hosted verification. A platform checks a badge by fetching its assertion from Hyperstack.

| File | Open Badges type |
| - | - |
| `badge.json` | `Assertion`: one recipient's badge |
| `badgeclass.json` | `BadgeClass`: the badge, made from the credential group |
| `issuer.json` | `Issuer`: your organization |
| `badge.png` | The badge image |

Each file is at two URLs. Both return the same data:

```text theme={null}
https://api.thehyperstack.com/v1/openbadge/{key}/badge.json
https://api.thehyperstack.com/openbadge/v2/{key}/badge.json
```

The `id` and the links inside the JSON always use the `/v1/openbadge/` form. Any other file name redirects to `badge.json`.

> [!NOTE]
>
> Open Badges 2.0 works only when the credential's group has a badge design. If the group has no badge design, every 2.0 URL returns `404`.

### Assertion

```bash theme={null}
curl "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.json"
```

```json theme={null}
{
  "id": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.json",
  "@context": "https://w3id.org/openbadges/v2",
  "type": "Assertion",
  "recipient": {
    "type": "email",
    "salt": "8HcjfhK7NhvkHRPh",
    "hashed": true,
    "identity": "sha256$5b24499078a6a86c07ee875f5a41156e493a9b3773ba9299aa88be5d4f0ae151"
  },
  "badge": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badgeclass.json",
  "verification": {
    "type": "hosted",
    "url": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.json"
  },
  "issuedOn": "2026-03-15T10:30:04.887215+00:00",
  "image": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.png",
  "evidence": null,
  "expires": "2028-03-15T10:30:04.887215+00:00"
}
```

**recipient** `object`

The recipient's email address, hashed. The hash is SHA-256 of the email address followed by the `salt`. To check an email address, hash it the same way and compare the result with the part of `identity` after `sha256$`.

**issuedOn** `string`

The credential's issue date, in UTC. This is the issue date you set on the credential. If you did not set one, it is the publish date. The credential page shows the same date.

**expires** `string`

The credential's expiry date. It is the same date as on the credential page. If you change the expiry date, this changes too. When the credential does not expire, there is no `expires` field.

### Badge class

```json theme={null}
{
  "id": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badgeclass.json",
  "@context": "https://w3id.org/openbadges/v2",
  "type": "BadgeClass",
  "name": "Cloud Security Fundamentals",
  "description": "Learn the basics of securing cloud workloads.",
  "image": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.png",
  "criteria": "https://acme.example/courses/cloud-security",
  "issuer": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/issuer.json",
  "tags": ["cloud", "security"]
}
```

`name` and `description` come from the credential group. `criteria` is the group's website. `tags` are the group's skill tags. It is an empty list when the group has no tags.

### Issuer

```json theme={null}
{
  "id": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/issuer.json",
  "@context": "https://w3id.org/openbadges/v2",
  "type": "Issuer",
  "name": "Acme Academy",
  "url": "https://acme.example",
  "description": null,
  "email": "certificates@acme.example"
}
```

`url` is your organization's website. If you have not set one, it is your organization page, such as `https://hyperstack.id/acme-academy`. `email` is your organization's contact email. If you have not set one, it is `connect@thehyperstack.com`.

### Badge image

`badge.png` returns a PNG file as a download. The image is the credential's own badge. If the credential has no badge image of its own, it is the group's badge design. If there is neither, it returns `404`.

The PNG is baked: it holds the assertion URL in a PNG text chunk named `openbadges`. A tool that reads the image can find the assertion from this URL.

## Open Badges 3.0

Open Badges 3.0 credentials are W3C Verifiable Credentials of type `OpenBadgeCredential`.

| URL | Returns |
| - | - |
| `/openbadge/v3/{key}/credential.json` | The credential with a `proof`. `badge.json` returns the same. |
| `/openbadge/v3/{key}` | The same credential without a `proof`. |
| `/openbadge/v3/{key}/achievement.json` | The `Achievement`, made from the credential group. |
| `/openbadge/v3/{key}/issuer.json` | The issuer `Profile` of your organization. |
| `/openbadge/v3/{key}/revocation_list.json` | The revocation list of the credential. |
| `/openbadge/v3/{key}/badge.png` | The badge image, baked with the signed credential. |

Any other file name returns the credential without a `proof`.

### Signed credential

```bash theme={null}
curl "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/credential.json"
```

```json theme={null}
{
  "@context": [
    "https://www.w3.org/ns/credentials/v2",
    "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
    "https://purl.imsglobal.org/spec/ob/v3p0/extensions.json"
  ],
  "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f",
  "type": ["VerifiableCredential", "OpenBadgeCredential"],
  "credentialSchema": [
    {
      "id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json",
      "type": "1EdTechJsonSchemaValidator2019"
    }
  ],
  "name": "Jane Doe",
  "description": "Learn the basics of securing cloud workloads.",
  "issuer": {
    "id": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
    "type": ["Profile"],
    "name": "Acme Academy",
    "image": {
      "id": "https://hyperstack.id/display-picture/organization/acme-academy",
      "type": "Image",
      "caption": "Logo of Acme Academy"
    },
    "url": "https://acme.example",
    "description": "Acme Academy teaches cloud and security skills.",
    "email": "certificates@acme.example"
  },
  "credentialSubject": {
    "type": ["AchievementSubject"],
    "identifier": [
      {
        "type": "IdentityObject",
        "hashed": true,
        "identityHash": "sha256$86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d",
        "identityType": "emailAddress"
      }
    ],
    "achievement": {
      "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/achievement.json",
      "type": ["Achievement"],
      "name": "Cloud Security Fundamentals",
      "description": "Learn the basics of securing cloud workloads.",
      "achievementType": "Badge",
      "creator": {
        "id": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
        "type": ["Profile"],
        "name": "Acme Academy",
        "image": {
          "id": "https://hyperstack.id/display-picture/organization/acme-academy",
          "type": "Image",
          "caption": "Logo of Acme Academy"
        },
        "url": "https://acme.example",
        "description": "Acme Academy teaches cloud and security skills.",
        "email": "certificates@acme.example"
      },
      "criteria": {
        "id": "https://hyperstack.id/acme-academy/group/Kq7Zp2LmX9aB",
        "narrative": "Completed all required course components."
      },
      "image": {
        "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.png",
        "type": "Image"
      },
      "tag": ["cloud", "security"]
    },
    "source": {
      "id": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
      "type": ["Profile"],
      "name": "Acme Academy",
      "image": {
        "id": "https://hyperstack.id/display-picture/organization/acme-academy",
        "type": "Image",
        "caption": "Logo of Acme Academy"
      },
      "url": "https://acme.example",
      "description": "Acme Academy teaches cloud and security skills.",
      "email": "certificates@acme.example"
    }
  },
  "evidence": [
    {
      "type": ["Evidence"],
      "name": "Cloud Security Fundamentals",
      "genre": "PlainTextEvidence",
      "description": "This person has completed the Cloud Security Fundamentals course. Their achievement was validated by Acme Academy through course completion and real-world application, and against the criteria set by the organization."
    }
  ],
  "credentialStatus": {
    "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/revocation_list.json",
    "type": "1EdTechRevocationList"
  },
  "issuanceDate": "2026-03-15T10:30:04Z",
  "validFrom": "2026-03-15T10:30:04Z",
  "validUntil": "2028-03-15T10:30:04Z",
  "proof": [
    {
      "type": "DataIntegrityProof",
      "created": "2026-10-20T08:12:45Z",
      "verificationMethod": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU#key-0",
      "cryptosuite": "eddsa-rdfc-2022",
      "proofPurpose": "assertionMethod",
      "proofValue": "z62QHgzAtc1RHy63f2Bqq18jacmPL3K9c5w9sYpNBzwSt7jeAzUDYnuzuMJPavm2R3YSjRGcuXeXzDFtJswo2JPui"
    }
  ]
}
```

**name** `string`

The recipient's name.

**description** `string`

The group's description, without HTML.

**issuer** `object`

Your organization. `id` is your [issuer DID](#issuer-did). `url` is your website, or your organization page if you have not set one. `email` is your organization's email, or `connect+{your-org-name}@thehyperstack.com` if you have not set one.

**credentialSubject** `object`

The recipient and what they earned.

**properties**

**identifier** `array`

The recipient's email address, hashed. `identityHash` is `sha256$` followed by the SHA-256 of the email address. There is no salt.

**achievement** `object`

The badge, made from the credential group: its title, description, badge image and tags. `achievementType` is always `Badge`.

**source** `object`

The issuer profile, the same as `issuer`.

**issuanceDate** `string`

The credential's issue date, in UTC: the issue date you set, or the publish date if you did not set one.

**validFrom** `string`

The same date as `issuanceDate`. It does not change between requests.

**validUntil** `string`

The credential's expiry date, in UTC. It is the same date as on the credential page. If you change the expiry date, this changes too. When the credential does not expire, there is no `validUntil` field.

**proof** `array`

The signature. Only `credential.json` and `badge.json` have it.

Some values are the same for every credential:

* `criteria.narrative` is always `Completed all required course components.`
* The `evidence` text is always made from the group title and your organization's name.

### Signature

Hyperstack signs the credential when you request it.

* The proof type is `DataIntegrityProof`, with the cryptosuite `eddsa-rdfc-2022`.
* The key is your organization's Ed25519 key. Each organization has its own key.
* `verificationMethod` is your issuer DID followed by `#key-0`. The public key is in your [DID document](#issuer-did).
* Each request makes a new proof. `created` and `proofValue` change every time.

### Achievement, issuer and revocation list

`achievement.json` and `issuer.json` return the `achievement` and `issuer` objects shown above, on their own.

`revocation_list.json` returns the credential's status. For a valid credential, `revoked` is `false`:

```json theme={null}
{
  "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/revocation_list.json",
  "issuer": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
  "revokedCredentials": [
    {
      "id": "https://api.thehyperstack.com/openbadge/v3/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f",
      "revoked": false
    }
  ]
}
```

For a revoked or suspended credential, `revoked` is `true` and there is a `revocationReason`. See [Revoked and suspended credentials](#revoked-and-suspended-credentials).

### Badge image

`badge.png` returns a PNG file as a download. The image is the group's badge design. If the group has no badge design, it returns `404`.

The PNG is baked: it holds the full signed credential as JSON, in a PNG text chunk named `openbadgecredential`.

## Issuer DID

Each organization has a DID that uses the `did:web` method:

```text theme={null}
did:web:api.thehyperstack.com:openbadge:v3:issuers:{issuer_id}
```

`{issuer_id}` is a fixed ID of your organization. To find your DID, open any of your credentials at `/openbadge/v3/{key}` and read `issuer.id`.

The DID document is at this URL. A `did:web` resolver turns the DID into this URL.

```text theme={null}
https://api.thehyperstack.com/openbadge/v3/issuers/{issuer_id}/did.json
```

The same document is also at the URL without `/did.json`:

```text theme={null}
https://api.thehyperstack.com/openbadge/v3/issuers/{issuer_id}
```

```bash theme={null}
curl "https://api.thehyperstack.com/openbadge/v3/issuers/Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU/did.json"
```

```json theme={null}
{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://www.w3.org/ns/credentials/v2",
    "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
    "https://w3id.org/security/multikey/v1"
  ],
  "type": ["Profile"],
  "id": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
  "name": "Acme Academy",
  "email": "certificates@acme.example",
  "image": "https://hyperstack.id/display-picture/organization/acme-academy",
  "url": "https://acme.example",
  "description": "Acme Academy teaches cloud and security skills.",
  "controller": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
  "assertionMethod": [
    "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU#key-0"
  ],
  "verificationMethod": [
    {
      "id": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU#key-0",
      "type": "Multikey",
      "controller": "did:web:api.thehyperstack.com:openbadge:v3:issuers:Xk3pQ9rT7vLm2NcB8dFh4JsW6yZa1GeU",
      "publicKeyMultibase": "z6MktRQkCJGc72JAzJrx3ekCrMtaSeGaj1bH7GYH4KuPrPpu"
    }
  ]
}
```

The key `#key-0` is a `Multikey`. `publicKeyMultibase` is your organization's Ed25519 public key. It is the key that signs your Open Badges 3.0 credentials. `email`, `url` and `description` come from your organization profile. They are empty when you have not set them. `description` keeps any HTML formatting from your profile.

## For recipients

Recipients can download the Open Badges 2.0 image from their credential page:

**1. Open the credential page**

Open `https://hyperstack.id/credential/{key}`.

**2. Show the badge**

If the credential has a certificate and a badge, click **Badge**.

**3. Download**

Click the download button at the top of the page. For most organizations, it is labelled **Print**.

The file is the baked `badge.png` from [Open Badges 2.0](#badge-image). The recipient can upload it to a platform that accepts Open Badges 2.0 images. The download button does not show when the credential is revoked or suspended.

## Revoked and suspended credentials

Open Badges has no "suspended" state. Hyperstack shows a suspended credential as revoked. When you lift the suspension, the credential is valid again at the same URLs.

| | Open Badges 2.0 `badge.json` | Open Badges 3.0 `revocation_list.json` |
| - | - | - |
| Valid | `200` with the full assertion | `200`, `"revoked": false` |
| Revoked | `410` with `"revoked": true` | `200`, `"revoked": true` |
| Suspended | `410` with `"revoked": true` | `200`, `"revoked": true` |

A revoked Open Badges 2.0 assertion returns HTTP `410 Gone`, as the Open Badges 2.0 standard says:

```json theme={null}
{
  "@context": "https://w3id.org/openbadges/v2",
  "id": "https://api.thehyperstack.com/v1/openbadge/9f1c2b3a-7d4e-4c5f-8a6b-2e3d4c5b6a7f/badge.json",
  "type": "Assertion",
  "revoked": true,
  "revocationReason": "Revoked by the issuer."
}
```

For a suspended credential, `revocationReason` is `Suspended by the issuer.` The Open Badges 3.0 revocation list uses the same reasons.

The other files (`badgeclass.json`, `issuer.json`, the 3.0 credential and the badge images) still return their data. Platforms find the status from `badge.json` (2.0) or from `credentialStatus` (3.0).

## Credential status

To check that a credential is still valid (not suspended, revoked or expired), use [View Credential](/docs/api-guide/view-credential), or open its credential page and click **Verify credential**.

## Errors

| Case | Result |
| - | - |
| The key is not a UUID | `404` |
| No credential has this key | `404` |
| The credential is a draft or under review | `404` |
| The credential's group has no badge design (all Open Badges 2.0 URLs, and the 3.0 `badge.png`) | `404` |
| The group's badge design was deleted and the credential has no badge image of its own (Open Badges 2.0 `badge.png`) | `404` |
| Your organization has no active signing key (Open Badges 3.0 and DID document) | `404` |
| No organization has this issuer ID (DID document) | `404` |
| The credential is revoked or suspended (Open Badges 2.0 `badge.json`) | `410`, with JSON |

A `404` returns an HTML page, not JSON.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.