OpenConsultant · Draft specification 0.1.0
Specification
How a consulting company publishes its consultant profiles as JSON on its own domain, and how clients, agencies and matching services find and read them at the source.
Draft specification, version 0.1.0. This is an experimental first version, published to be tried out and criticised. Details may change before 1.0. Changes are listed in the changelog.
Overview
OpenConsultant defines two kinds of JSON documents and where to put them.
- An entry file at a fixed address,
/openconsultant.json, which names the publisher and lists its profiles. - One profile per consultant, each at a URL of its own.
Both are ordinary files served over HTTPS. There is no API to implement, no registration and no central database.
The consulting company that offers the consultant publishes the profile on its own website. Everyone else reads it from there and links back to it. The information stays at the source, and the market connects around it.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as described in RFC 2119.
Terms
- Publisher: the consulting company that offers the consultant and publishes the profile on its own domain. The publisher is the source of the information.
- Consumer: any service that reads profiles in order to index, present or match them, such as a client’s supplier system, an agency’s platform, a search service or an AI agent.
- Profile: the JSON document describing one consultant.
- Source address: the permanent URL of the profile’s JSON document on the publisher’s domain, stated in
openconsultant.canonicalUrl. - Entry file: the JSON document that lists a publisher’s profiles.
Machine readable definitions
- Profile schema:
/schemas/v0.1/schema.json - Entry file schema:
/schemas/v0.1/manifest.schema.json
The schemas are written in JSON Schema, draft 2020-12. Where this text and a schema disagree, the schema is the mistake: please report it.
The entry file
A publisher MUST serve the entry file at the root of the domain it publishes for:
https://example.com/openconsultant.json
{
"$schema": "https://openconsultant.io/schemas/v0.1/manifest.schema.json",
"standard": "OpenConsultant",
"schemaVersion": "0.1.0",
"publisher": {
"name": "Example Consulting",
"url": "https://openconsultant.io/examples/"
},
"updatedAt": "2026-10-01T08:00:00Z",
"consultants": [
{
"id": "urn:uuid:6f1d2c3a-8b4e-4f7a-9c21-5d0e7a3b9f10",
"url": "https://openconsultant.io/examples/profile.json",
"updatedAt": "2026-10-01T08:00:00Z",
"htmlUrl": "https://openconsultant.io/examples/#profile"
}
]
}| Field | Type | Status | Description |
|---|---|---|---|
$schema | string (uri) | Optional | URL of the schema this document follows. |
standard | "OpenConsultant" | Required | Name of the standard. |
schemaVersion | string | Required | Version of OpenConsultant the entry file follows, for example "0.1.0". |
publisher | object | Required | The consulting company that publishes the profiles on this domain. |
publisher.name | string | Required | Name of the organisation. |
publisher.url | string (uri) | Optional | The organisation's website. |
updatedAt | string (date-time) | Required | When the entry file or any listed profile last changed. |
consultants | array of object | Required | One entry per published profile. An empty list is valid. |
consultants[].id | string | Required | The profile's stable identifier, identical to openconsultant.id in the profile. |
consultants[].url | string (uri) | Required | The profile's source address, identical to openconsultant.canonicalUrl in the profile. |
consultants[].updatedAt | string (date-time) | Optional | When the profile last changed. Lets clients skip profiles they already have. |
consultants[].htmlUrl | string (uri) | Optional | URL of the human readable profile page. |
Rules for the entry file:
- Every
urlMUST be absolute, use HTTPS and equalopenconsultant.canonicalUrlin the profile it points to. - Every
idMUST equalopenconsultant.idin the profile it points to. updatedAtMUST change whenever a profile is added, changed or removed.- A profile that is no longer offered MUST be removed from the list.
The profile
A profile is a JSON Resume document with one extra top-level object, openconsultant. JSON Resume allows additional properties, so a profile remains a valid JSON Resume document and works with existing JSON Resume tools.
Resume content
OpenConsultant reuses the JSON Resume sections without changing them. The ones that matter most for consulting are listed below. Any other JSON Resume section MAY be included.
| Section | Contains |
|---|---|
basics |
Name, role, profile text and contact information |
work |
Professional experience |
skills |
Competences, each with name, optional level and keywords |
education |
Education |
certificates |
Certifications |
projects |
Reference projects |
languages |
Spoken languages |
Only basics.name is required from the resume content.
| Field | Type | Status | Description |
|---|---|---|---|
basics.name | string | Required | The consultant's name as it should be presented. |
basics.label | string | Optional | Professional role or title, for example "Senior Frontend Developer". |
basics.summary | string | Optional | Short profile text. |
basics.image | string | Optional | URL of a portrait. Publish only with the person's knowledge. |
basics.email | string (email) | Optional | Contact address. Never required. Prefer openconsultant.contact for contact through the company. |
basics.phone | string | Optional | Phone number. Never required. |
basics.url | string (uri) | Optional | A personal or professional web page. |
basics.location | object | Optional | Where the consultant is based, in JSON Resume form (city, region, countryCode). |
The openconsultant object
Everything specific to consulting lives in the openconsultant object: who offers the consultant, when they are available, where and how they work.
| Field | Type | Status | Description |
|---|---|---|---|
openconsultant.schemaVersion | string | Required | Version of OpenConsultant the profile follows, for example "0.1.0". |
openconsultant.id | string | Required | Stable identifier of the profile, unique within the publisher. Must never change, even if the consultant's name or the file name does. A UUID URN is recommended. |
openconsultant.canonicalUrl | string (uri) | Required | The source address: the permanent URL of this JSON document on the company's own domain. Every service that presents the profile links back to it. |
openconsultant.company | object | Required | The consulting company that offers the consultant and publishes the profile. The company is the source of the information. |
openconsultant.company.name | string | Required | Name of the organisation. |
openconsultant.company.url | string (uri) | Optional | The organisation's website. |
openconsultant.availability | object | Required | Whether the consultant can take on new work. |
openconsultant.availability.status | "available" | "available-from" | "busy" | "unknown" | Required | available: free now. available-from: free from the date in availableFrom. busy: not available. unknown: the publisher does not state availability. |
openconsultant.availability.availableFrom | string (date) | Optional | First day the consultant is available. Required when status is available-from. |
openconsultant.locations | array of object | Optional | Geographic areas where the consultant can work on site. |
openconsultant.locations[].city | string | Optional | City or town. |
openconsultant.locations[].region | string | Optional | Region, county or state. |
openconsultant.locations[].countryCode | string | Optional | ISO 3166-1 alpha-2 country code, for example "SE". |
openconsultant.remote | "onsite" | "hybrid" | "remote" | Optional | Preferred way of working. onsite: at the client. hybrid: a mix of on-site and remote. remote: fully remote. |
openconsultant.engagementTypes | array of "full-time" | "part-time" | "project" | Optional | Forms of engagement the consultant is offered for. |
openconsultant.contact | object | Optional | How to enquire about the consultant, normally through the company rather than the individual. |
openconsultant.contact.name | string | Optional | Person or function to contact. |
openconsultant.contact.email | string (email) | Optional | Contact address at the company. |
openconsultant.contact.url | string (uri) | Optional | Contact page or enquiry form. |
openconsultant.updatedAt | string (date-time) | Required | When the profile was last changed, including changes to availability. |
openconsultant.htmlUrl | string (uri) | Optional | URL of the human readable profile page at the company showing the same information. |
A profile with only the required fields looks like this:
{
"basics": {
"name": "Erik Example"
},
"openconsultant": {
"schemaVersion": "0.1.0",
"id": "urn:uuid:2b9c1e54-7a3d-4c8f-b6e0-91f4d2a7c355",
"canonicalUrl": "https://openconsultant.io/examples/profile.minimal.json",
"company": {
"name": "Example Consulting"
},
"availability": {
"status": "unknown"
},
"updatedAt": "2026-10-01T08:00:00Z"
}
}The source
A profile has one original, and it lives with the consulting company that published it. Everything else in this specification follows from that.
The publisher is the source. The consulting company writes the profile, serves it from its own domain and keeps it current. Only the publisher changes it. openconsultant.company names the publisher.
The source address is permanent. openconsultant.canonicalUrl is the address of the original. It MUST be an HTTPS URL on a domain the publisher controls, MUST be the address the document is actually served from, and SHOULD NOT change during the lifetime of the profile.
Consumers build on the source. Clients, agencies, platforms and other services are free to index profiles, present them, match them against assignments and build services around them. They do so from the original, link back to it and follow its updates, so that nobody has to maintain a separate version of a consultant’s CV. The rules are in Reading profiles.
Identity
A profile is identified by openconsultant.id, not by its URL and not by the consultant’s name.
- The
idMUST be unique within the publisher and MUST NOT change during the lifetime of the profile. - The
idMUST NOT be reused for another person. - A random UUID written as a URN, such as
urn:uuid:6f1d2c3a-8b4e-4f7a-9c21-5d0e7a3b9f10, is recommended. Do not derive theidfrom a name, an email address or a national identity number.
If a consultant changes name, or the file is moved, the id stays the same and consumers can keep following the same profile.
Availability and freshness
Availability is the information that ages fastest, so it is the main reason to read a profile again.
availability.statusMUST be one ofavailable,available-from,busyorunknown.- When the status is
available-from,availableFromMUST contain the first available date. updatedAtMUST change whenever anything in the profile changes, including availability.- Publishers who do not want to state availability use
unknown. Consumers MUST NOT presentunknownas available.
Consumers SHOULD show when a profile was last updated and SHOULD read the entry file again regularly. Once a day is a reasonable default.
Human readable pages
The same information SHOULD also exist as an ordinary web page. Link the two together:
- In the profile, set
openconsultant.htmlUrlto the page. - On the page, point to the JSON with a link element:
<link rel="alternate" type="application/json" href="https://example.com/consultants/anna-andersson.json" />
The page and the JSON SHOULD be generated from the same source, so they cannot drift apart.
Serving the files
- Files MUST be served over HTTPS.
- Files MUST be retrievable with a plain
GETrequest, without authentication, cookies or JavaScript. - The
Content-TypeMUST beapplication/json. - Files SHOULD allow cross-origin reads by sending
Access-Control-Allow-Origin: *. - Files MUST be encoded as UTF-8.
Reading profiles
A consumer finds and reads a publisher’s profiles in three steps.
- Fetch
https://<domain>/openconsultant.json. - Check that
standardisOpenConsultantand that it understands theschemaVersion. - Fetch each
urlinconsultants. UseupdatedAtto skip profiles that have not changed.
Consumers:
- MUST ignore fields they do not recognise.
- MUST stop presenting a profile that has disappeared from the entry file.
- MUST keep
canonicalUrlwith every copy and, wherever they present the profile, link back to the original: tohtmlUrlwhen there is one, otherwise tocanonicalUrl. - MUST name the publishing company as the source, and MUST NOT present themselves as the origin of the profile.
- MUST replace their copy when the original changes, and SHOULD NOT alter the content they present.
- SHOULD direct enquiries to
openconsultant.contact. - SHOULD identify themselves with a descriptive
User-Agent.
Consumers MAY add their own services around a profile, such as matching, evaluation or advice, as long as it stays clear what comes from the publisher and what the consumer has added.
Discovery in this version relies only on the fixed address. Later versions may add other mechanisms, such as .well-known or HTML metadata, without removing this one.
Extending the format
Unknown fields are allowed everywhere. Publishers MAY add their own. To avoid clashes with future versions of the standard, custom fields SHOULD start with x-, for example x-hourlyRateBand.
Versioning
- Versions follow Semantic Versioning. This is
0.1.0. schemaVersionstates which version a document follows.- Each published minor version of the schemas has a permanent URL, for example
/schemas/v0.1/schema.json. Published schema files are not changed in incompatible ways. - Additions that old consumers can safely ignore are made within a version line. Breaking changes get a new version and a new schema URL.
Privacy and responsibility
A profile describes a person, and everything published according to this standard is public. The publisher is the one who decides what to publish and is responsible for doing it lawfully. That is the point of publishing at the source: the company that owns the information stays in control of it.
Personal data. Within the EU and EEA the GDPR applies. The publisher needs a legal basis for publishing, such as the consultant’s consent or another basis that fits the situation, and must have told the consultant clearly what is published, where and why. OpenConsultant does not provide that basis. It only describes the format.
Contact details. No private contact details are required. basics.email and basics.phone are optional and are best left out. Use openconsultant.contact to route enquiries through the company.
What not to publish. Do not publish national identity numbers, home addresses, dates of birth, salary or rate agreements, client names covered by confidentiality, or anything else the consultant or a client has not agreed to make public. Never put secrets, API keys or internal identifiers from business systems in a profile.
Updating and unpublishing. When a consultant leaves, or withdraws their agreement, remove the profile from the entry file and stop serving the profile file. A removed file SHOULD answer with HTTP 404 or 410.
Reuse. Consumers that index or present profiles are responsible for their own processing of personal data. They MUST follow removals, MUST link back to the original, SHOULD refresh copies regularly and SHOULD NOT keep a profile after the publisher has withdrawn it.