ReferencePlatform API
Platform API#
What does each platform route take and answer, and how is a call signed?
On this page
A platform is a business that serves its own users through Mehen (Platforms and managed workspaces). It calls the vault at https://app.mehen.dev, under /v1/platform, with JSON in and out. Build on Mehen walks through the calls in order; @mehen/client's platform client makes every one of them for you. The tables on this page are generated from the route table in Mehen's protocol, so they say what the vault named in the footer accepts.
Signing a call#
Every call carries a Mehen-Platform-Proof header: a compact JWS, signed with the platform's own P-256 key, over that one request. There is no bearer token, so nothing on the platform's disk or in a log can be replayed.
header { "alg": "ES256", "typ": "mehen-platform+jwt", "kid": "pky_..." }
payload { "plt": "plt_...", "sub": "<subject>", "htm": "POST", "htu": "https://app.mehen.dev/v1/platform/...", "bh": "<base64url SHA-256 of the body>", "iat": 1793270400, "jti": "<random>" }| Claim | What it is |
|---|---|
kid | The platform key's id, pky_..., which Mehen's operator gave the platform when it registered the key. |
plt | The platform's id, plt_.... It must be the key's own platform. |
sub | The subject the path names, on every route under /v1/platform/subjects/:subject, and absent on every other route. It must equal the path's subject. |
htm, htu | The method, and the full URL with its query. |
bh | The base64url SHA-256 of the exact body bytes, or of nothing for a request with no body. |
iat | When it was signed, in seconds. The vault accepts 60 seconds either way. |
jti | A random nonce. The vault accepts each one once. |
The proof's typ and header differ from a machine's Mehen-Proof, so neither can stand in for the other, and a platform call that also carries Mehen-Proof is refused with bad_request. The vault checks the proof in this order: the key by its id, with no cache; the signature, method, URL, body hash, time and subject; the platform's status; then the nonce, spent once per key, and the platform's limits. Keys, proofs and signing has the details.
Subjects#
A subject is the platform's own name for one of its users: 8 to 128 of A-Z a-z 0-9 _ -. Mehen never interprets it, and it is unique within its platform only: the vault finds a workspace from the verified platform and the subject together, so another platform naming the same string reaches its own workspace or none. Choose a subject that says nothing about the person, and never reuse one.
Answers and refusals#
A success is the JSON the route's table gives. Answers may gain members in later releases; read what you know and ignore the rest. No answer carries a secret value, a client secret, a token, a link to a Mehen page or a command to run.
A refusal is the error object of Errors and exit codes, with the header Mehen-Error: 1 and the HTTP status that page gives its code. Two codes are only ever a platform's: platform_suspended and quota_exceeded.
In a managed workspace, a refusal a machine gets on the ordinary machine routes carries no link: approval_required names the approval and its binding code for the platform to decide, and needs_reauth says the platform reconnects the account. HTTP surfaces lists how each machine route answers there.
Routes#
Every route is under /v1/platform and carries Mehen-Platform-Proof. In the tables below, a nested member such as a.b is required within its parent, a[].b is a member of each item of the array a, and a.<name>.b a member of each value of the map a. The platform's own routes:
| Route | Method and path | What it does |
|---|---|---|
platform.view | GET /v1/platform | Reads the platform as Mehen holds it: its status, limits, defaults and their revision, how many subjects it has, and its keys. |
subjects.list | GET /v1/platform/subjects | Lists the platform's own subjects, a page at a time, with each one's status. |
defaults.put | PUT /v1/platform/defaults | Replaces the platform's defaults: which templates and providers its users may add, default OAuth scopes and tags, rules added to a template's or provider's policy, and whether keys with no template may be read without a hold. |
subjects.removeAll | DELETE /v1/platform/subjects | Deletes every subject the platform has, each as deleting one does, 50 at a time. |
One subject's routes, each under /v1/platform/subjects/:subject:
| Route | Method and path | What it does |
|---|---|---|
subjects.ensure | PUT /v1/platform/subjects/:subject | Makes sure the subject has its managed workspace: 201 when it is made, 200 when it already existed. |
subjects.get | GET /v1/platform/subjects/:subject | Reads one subject: its status and how many devices, items and projects its workspace holds. |
subjects.remove | DELETE /v1/platform/subjects/:subject | Deletes a subject: every device is revoked, every secret and item deleted, and the audit kept until the purge date in the answer, when the whole workspace is erased. |
devices.create | POST /v1/platform/subjects/:subject/devices | Provisions a device in the subject's workspace for one of the platform's agents, with no enrolment page. |
devices.list | GET /v1/platform/subjects/:subject/devices | Lists the subject's devices, with when each was last seen and how many live workloads it has. |
devices.revoke | DELETE /v1/platform/subjects/:subject/devices/:device | Revokes a device. |
items.list | GET /v1/platform/subjects/:subject/items | Lists the subject's items by Mehen's metadata (name, kind, provider, account, tags, hosts, status and policy). |
items.remove | DELETE /v1/platform/subjects/:subject/items/:slug | Deletes an item and its secrets. |
intakes.create | POST /v1/platform/subjects/:subject/intakes | Opens a request for a credential and answers the vault's seal key and the fields to fill in: a key from a catalogue template or for one host, or the person's own OAuth app, for a catalogue provider (with its set-up steps) or with its own provider definition. |
intakes.submit | POST /v1/platform/subjects/:subject/intakes/:intake/submit | Submits the sealed values for an open request. |
intakes.cancel | POST /v1/platform/subjects/:subject/intakes/:intake/cancel | Cancels an open request for a credential. |
connects.start | POST /v1/platform/subjects/:subject/connects | Starts connecting an account with the person's own OAuth app, held in their workspace, and answers the provider's authorisation URL, carrying Mehen's state, the platform's callback and a PKCE challenge where the provider supports one, for the platform to send the person to. |
connects.finish | POST /v1/platform/subjects/:subject/connects/finish | Finishes a connection with what the platform's callback received. |
connects.get | GET /v1/platform/subjects/:subject/connects/:connect | Reads a connection's progress and outcome. |
connects.cancel | POST /v1/platform/subjects/:subject/connects/:connect/cancel | Cancels a connection that has not finished. |
approvals.list | GET /v1/platform/subjects/:subject/approvals | Lists the subject's held requests, filtered by status. |
approvals.get | GET /v1/platform/subjects/:subject/approvals/:approval | Reads one held request: the exact request it would send, its digest, the device and workload that asked, what Mehen could not read, and who decided it. |
approvals.decide | POST /v1/platform/subjects/:subject/approvals/:approval/decide | Approves or denies a held request, naming its digest. |
projects.create | POST /v1/platform/subjects/:subject/projects | Creates a project, a named set of settings and keys for computers, with its environments. |
projects.get | GET /v1/platform/subjects/:subject/projects/:project | Reads a project and its variables, plain values included. |
projects.setVar | PUT /v1/platform/subjects/:subject/projects/:project/vars/:name | Sets one variable in one environment to a plain value or an item's field, or unsets it when neither is sent. |
projects.remove | DELETE /v1/platform/subjects/:subject/projects/:project | Deletes a project. |
audit.read | GET /v1/platform/subjects/:subject/audit | Reads the subject's audit chain a page at a time, every row, with its head and whether it verified. |
workspace.freeze | POST /v1/platform/subjects/:subject/freeze | Freezes the subject's workspace: nothing is used or changed until it is unfrozen. |
workspace.unfreeze | POST /v1/platform/subjects/:subject/unfreeze | Unfreezes the subject's workspace. |
Common refusals#
Any route may answer these, beyond its own. Every refusal is an error object with the header Mehen-Error: 1.
error | When |
|---|---|
unauthenticated | No proof, or its key is unknown, revoked, past its rotation date or not this platform's. |
proof_invalid | The proof's signature, time, method, URL, body hash or subject does not match the request, or its nonce was used before. |
platform_suspended | Mehen's operator suspended or retired the platform. |
rate_limited | Too many calls or decisions for the platform or the subject; wait retryAfter seconds. |
bad_request | The path, query or body does not have the shape this page gives. |
not_found | The subject, or the object the path names, is not this platform's, exactly as if it did not exist. |
Limits#
Mehen's operator sets each platform's limits when it registers the platform, and may change them later. Every managed workspace reads them live; past one, a route answers quota_exceeded or rate_limited. The defaults:
| Limit | Default | Range | What it limits |
|---|---|---|---|
subjects | 1,000 | 1 to 10,000,000 | How many subjects (users) the platform may have at once, counting those still being deleted. |
devicesPerSubject | 16 | 1 to 1,000 | Live devices in one subject's workspace. A revoked device no longer counts. |
itemsPerSubject | 100 | 1 to 10,000 | Credentials in one subject's workspace: keys, OAuth apps and connections together. |
projectsPerSubject | 16 | 1 to 1,000 | Projects in one subject's workspace. Each environment holds up to 500 variables, as everywhere in Mehen. |
openRequestsPerSubject | 10 | 1 to 100 | Requests for a credential and connections the platform has started in one subject's workspace and not yet finished or cancelled. |
callsPerMinute | 600 | 1 to 100,000 | Platform calls a minute, across all of the platform's subjects. A call is counted once its proof is verified; past it, rate_limited. |
subjectWritesPerMinute | 60 | 1 to 10,000 | Calls that change something, a minute, in one subject's workspace. |
decisionsPerMinute | 30 | 1 to 10,000 | Approvals and denials a minute, across all of the platform's subjects. |
subjectDecisionsPerMinute | 10 | 1 to 1,000 | Approvals and denials a minute, in one subject's workspace. |
decisionTtlSeconds | 900 | 60 to 900 | The longest an approval the platform gives may stay usable, in seconds. An approval always serves one use. |
auditRetentionDays | 30 | 0 to 90 | How long a deleted subject's audit is kept before the whole workspace is erased, in days. Secrets are deleted at once, whatever this says. |
The platform#
platform.view#
GET /v1/platformReads the platform as Mehen holds it: its status, limits, defaults and their revision, how many subjects it has, and its keys.
Answer (200).
| Field | Type | Required |
|---|---|---|
platform | object | yes |
platform.id | string matching ^plt_[A-Za-z0-9_-]{4,64}$ | yes |
platform.slug | string matching ^[a-z][a-z0-9-]{2,39}$ | yes |
platform.name | string | yes |
platform.status | one of active, suspended, retiring, retired | yes |
limits | object | yes |
limits.subjects | integer, 1 to 10,000,000 | yes |
limits.devicesPerSubject | integer, 1 to 1,000 | yes |
limits.itemsPerSubject | integer, 1 to 10,000 | yes |
limits.projectsPerSubject | integer, 1 to 1,000 | yes |
limits.openRequestsPerSubject | integer, 1 to 100 | yes |
limits.callsPerMinute | integer, 1 to 100,000 | yes |
limits.subjectWritesPerMinute | integer, 1 to 10,000 | yes |
limits.decisionsPerMinute | integer, 1 to 10,000 | yes |
limits.subjectDecisionsPerMinute | integer, 1 to 1,000 | yes |
limits.decisionTtlSeconds | integer, 60 to 900 | yes |
limits.auditRetentionDays | integer, 0 to 90 | yes |
defaults | object | yes |
defaults.templates | array of string, 1 to 64 characters, at most 256 items | no |
defaults.providers | array of string, 1 to 64 characters, at most 64 items | no |
defaults.scopes | map of keys (string, 1 to 64 characters) to values (array of string, 1 to 200 characters, at most 50 items) | yes |
defaults.tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 16 items | yes |
defaults.customReads | one of allow, approve | yes |
defaults.rules | map of keys (string, 1 to 64 characters) to values (array of object, at most 64 items) | yes |
defaults.rules.<name>[].action | one of allow, approve, deny | yes |
defaults.rules.<name>[].methods | array of string, 1 to 16 characters, at most 16 items | no |
defaults.rules.<name>[].paths | array of string, at most 256 characters, at most 32 items | no |
defaults.rules.<name>[].body | one of graphql-mutation, graphql-query | no |
defaults.rules.<name>[].note | string, at most 200 characters | no |
defaultsRevision | integer, at least 1 | yes |
usage | object | yes |
usage.subjects | integer | yes |
keys | array of object | yes |
keys[].id | string matching ^pky_[A-Za-z0-9_-]{4,64}$ | yes |
keys[].createdAt | time (ISO 8601) | yes |
keys[].expiresAt | time (ISO 8601), or null | yes |
keys[].revoked | boolean | yes |
Its own refusals: none beyond the common ones.
subjects.list#
GET /v1/platform/subjectsLists the platform's own subjects, a page at a time, with each one's status. Another platform's subjects are never listed.
Query. Every value is a string on the wire.
| Parameter | Type | Required |
|---|---|---|
cursor | string, 1 to 256 characters | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
subjects | array of object, at most 100 items | yes |
subjects[].subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
subjects[].status | one of creating, active, deleting, deleted | yes |
subjects[].createdAt | time (ISO 8601) | yes |
next | string, or null | yes |
Its own refusals: none beyond the common ones.
defaults.put#
PUT /v1/platform/defaultsReplaces the platform's defaults: which templates and providers its users may add, default OAuth scopes and tags, rules added to a template's or provider's policy, and whether keys with no template may be read without a hold. Rules may only hold or refuse, never allow. Every workspace uses the new defaults within 30 seconds, and approvals made under the old ones go stale.
Body.
| Field | Type | Required |
|---|---|---|
templates | array of string, 1 to 64 characters, at most 256 items | no |
providers | array of string, 1 to 64 characters, at most 64 items | no |
scopes | map of keys (string, 1 to 64 characters) to values (array of string, 1 to 200 characters, at most 50 items) | yes |
tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 16 items | yes |
customReads | one of allow, approve | yes |
rules | map of keys (string, 1 to 64 characters) to values (array of object, at most 64 items) | yes |
rules.<name>[].action | one of allow, approve, deny | yes |
rules.<name>[].methods | array of string, 1 to 16 characters, at most 16 items | no |
rules.<name>[].paths | array of string, at most 256 characters, at most 32 items | no |
rules.<name>[].body | one of graphql-mutation, graphql-query | no |
rules.<name>[].note | string, at most 200 characters | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
defaults | object | yes |
defaults.templates | array of string, 1 to 64 characters, at most 256 items | no |
defaults.providers | array of string, 1 to 64 characters, at most 64 items | no |
defaults.scopes | map of keys (string, 1 to 64 characters) to values (array of string, 1 to 200 characters, at most 50 items) | yes |
defaults.tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 16 items | yes |
defaults.customReads | one of allow, approve | yes |
defaults.rules | map of keys (string, 1 to 64 characters) to values (array of object, at most 64 items) | yes |
defaults.rules.<name>[].action | one of allow, approve, deny | yes |
defaults.rules.<name>[].methods | array of string, 1 to 16 characters, at most 16 items | no |
defaults.rules.<name>[].paths | array of string, at most 256 characters, at most 32 items | no |
defaults.rules.<name>[].body | one of graphql-mutation, graphql-query | no |
defaults.rules.<name>[].note | string, at most 200 characters | no |
defaultsRevision | integer, at least 1 | yes |
Its own refusals: none beyond the common ones.
The table gives a rule's action as the policy shape allows it; a platform's rule may only be approve or deny, and an allow rule is refused with bad_request. So are a template, provider or scope the catalogue lacks, and a tag given twice.
subjects.removeAll#
DELETE /v1/platform/subjectsDeletes every subject the platform has, each as deleting one does, 50 at a time. confirm must be the platform's own slug. The platform stays registered.
Body.
| Field | Type | Required |
|---|---|---|
confirm | string matching ^[a-z][a-z0-9-]{2,39}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
deleting | integer | yes |
Its own refusals: none beyond the common ones.
A subject#
Every route here names one subject in its path, and its proof's sub claim must be that same subject. A subject the platform does not have is not_found, exactly as one that never existed.
subjects.ensure#
PUT /v1/platform/subjects/:subjectMakes sure the subject has its managed workspace: 201 when it is made, 200 when it already existed. Refused while the subject is still being deleted, and while the platform is retiring.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body. {}
Answer (200 or 201).
| Field | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
status | one of creating, active, deleting, deleted | yes |
createdAt | time (ISO 8601) | yes |
counts | object | yes |
counts.devices | integer | yes |
counts.items | integer | yes |
counts.projects | integer | yes |
Its own refusals, beyond the common ones: quota_exceeded, conflict.
subjects.get#
GET /v1/platform/subjects/:subjectReads one subject: its status and how many devices, items and projects its workspace holds. A deleted subject is answered until its purge, so its audit can still be read.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
status | one of creating, active, deleting, deleted | yes |
createdAt | time (ISO 8601) | yes |
counts | object | yes |
counts.devices | integer | yes |
counts.items | integer | yes |
counts.projects | integer | yes |
Its own refusals: none beyond the common ones.
subjects.remove#
DELETE /v1/platform/subjects/:subjectDeletes a subject: every device is revoked, every secret and item deleted, and the audit kept until the purge date in the answer, when the whole workspace is erased. Repeating it answers the same.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body. {}
Answer (200).
| Field | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
status | deleted | yes |
purgeAfter | time (ISO 8601) | yes |
Its own refusals: none beyond the common ones.
devices.create#
POST /v1/platform/subjects/:subject/devicesProvisions a device in the subject's workspace for one of the platform's agents, with no enrolment page. It may only relay and serve placeholders, and acts only through workloads. The same key again answers the existing device with 200.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
name | string, 1 to 64 characters | yes |
jwk | object | yes |
jwk.kty | EC | yes |
jwk.crv | P-256 | yes |
jwk.x | string matching ^[A-Za-z0-9_-]{43}$ | yes |
jwk.y | string matching ^[A-Za-z0-9_-]{43}$ | yes |
modes | array of one of relay, placeholder, 1 to 2 items | yes |
label | string matching ^[A-Za-z0-9_.:-]{1,120}$ | no |
Answer (200 or 201).
| Field | Type | Required |
|---|---|---|
device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
tenant | string matching ^tn_[A-Za-z0-9]{8,40}$ | yes |
name | string | yes |
label | string, or null | yes |
scope | object | yes |
scope.tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 32 items | yes |
scope.projects | array of string, 1 to 64 characters, at most 32 items | yes |
scope.maxEnvironment | one of unprotected, protected | yes |
scope.modes | array of one of relay, sign, mint, placeholder, lease, 1 to 5 items | yes |
scope.privateCredentials | boolean | yes |
scope.workloads | one of allowed, required | no |
createdAt | time (ISO 8601) | yes |
revoked | boolean | yes |
Its own refusals, beyond the common ones: quota_exceeded, conflict, frozen.
devices.list#
GET /v1/platform/subjects/:subject/devicesLists the subject's devices, with when each was last seen and how many live workloads it has.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
devices | array of object | yes |
devices[].device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
devices[].tenant | string matching ^tn_[A-Za-z0-9]{8,40}$ | yes |
devices[].name | string | yes |
devices[].label | string, or null | yes |
devices[].scope | object | yes |
devices[].scope.tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 32 items | yes |
devices[].scope.projects | array of string, 1 to 64 characters, at most 32 items | yes |
devices[].scope.maxEnvironment | one of unprotected, protected | yes |
devices[].scope.modes | array of one of relay, sign, mint, placeholder, lease, 1 to 5 items | yes |
devices[].scope.privateCredentials | boolean | yes |
devices[].scope.workloads | one of allowed, required | no |
devices[].createdAt | time (ISO 8601) | yes |
devices[].revoked | boolean | yes |
devices[].lastSeenAt | time (ISO 8601), or null | yes |
devices[].liveWorkloads | integer | yes |
Its own refusals: none beyond the common ones.
devices.revoke#
DELETE /v1/platform/subjects/:subject/devices/:deviceRevokes a device. Its workloads, open asks and runs end with it. Repeating it answers the same.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
tenant | string matching ^tn_[A-Za-z0-9]{8,40}$ | yes |
name | string | yes |
label | string, or null | yes |
scope | object | yes |
scope.tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 32 items | yes |
scope.projects | array of string, 1 to 64 characters, at most 32 items | yes |
scope.maxEnvironment | one of unprotected, protected | yes |
scope.modes | array of one of relay, sign, mint, placeholder, lease, 1 to 5 items | yes |
scope.privateCredentials | boolean | yes |
scope.workloads | one of allowed, required | no |
createdAt | time (ISO 8601) | yes |
revoked | boolean | yes |
Its own refusals, beyond the common ones: frozen.
items.list#
GET /v1/platform/subjects/:subject/itemsLists the subject's items by Mehen's metadata (name, kind, provider, account, tags, hosts, status and policy). Never a value.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Query. Every value is a string on the wire.
| Parameter | Type | Required |
|---|---|---|
kind | one of api-key, bearer-token, basic-auth, custom-headers, oauth-client, oauth-connection, google-service-account, github-app, hmac-key, ssh-key, password-login, totp, database-url, smtp-imap, cloud-access-key, file, note, generic | no |
tag | string matching ^[a-z0-9][a-z0-9:_-]{0,39}$ | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
items | array of object | yes |
items[].slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
items[].kind | one of api-key, bearer-token, basic-auth, custom-headers, oauth-client, oauth-connection, google-service-account, github-app, hmac-key, ssh-key, password-login, totp, database-url, smtp-imap, cloud-access-key, file, note, generic | yes |
items[].provider | string, or null | yes |
items[].account | string, or null | yes |
items[].accountVerified | boolean | yes |
items[].tags | array of string | yes |
items[].ceiling | one of relay, sign, mint, placeholder, lease, reveal | yes |
items[].hosts | array of string | yes |
items[].status | one of active, needs_reauth, refresh_retrying, disabled | yes |
items[].review | object | no |
items[].review.reason | string | yes |
items[].review.since | string | yes |
items[].updatedAt | string | yes |
items[].lastUsedAt | string, or null | no |
items[].policy | object | no |
items[].policy.default | one of allow, approve, deny | yes |
items[].policy.rules | array of object, at most 64 items | yes |
items[].policy.rules[].action | one of allow, approve, deny | yes |
items[].policy.rules[].methods | array of string, 1 to 16 characters, at most 16 items | no |
items[].policy.rules[].paths | array of string, at most 256 characters, at most 32 items | no |
items[].policy.rules[].body | one of graphql-mutation, graphql-query | no |
items[].policy.rules[].note | string, at most 200 characters | no |
Its own refusals: none beyond the common ones.
items.remove#
DELETE /v1/platform/subjects/:subject/items/:slugDeletes an item and its secrets. Approvals and grants on it end. An OAuth connection's token is handed back to the provider first where the provider allows it.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
deleted | true | yes |
providerRevoked | boolean | yes |
Its own refusals, beyond the common ones: frozen.
intakes.create#
POST /v1/platform/subjects/:subject/intakesOpens a request for a credential and answers the vault's seal key and the fields to fill in: a key from a catalogue template or for one host, or the person's own OAuth app, for a catalogue provider (with its set-up steps) or with its own provider definition. The person types the values into the platform's own page, which seals each secret in their browser before it reaches the platform. An existing name of the same kind is a rotation and keeps where the key may go. It lives 10 minutes and carries no link.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
service | object, one of 3 shapes | yes |
service.template | string, 1 to 64 characters | in its shape |
service.host | string | in its shape |
service.customProvider | object | in its shape |
service.customProvider.name | string, 1 to 64 characters | yes |
service.customProvider.authorizeUrl | URL, at most 2048 characters | yes |
service.customProvider.tokenUrl | URL, at most 2048 characters | yes |
service.customProvider.scopes | array of string matching ^[\x21\x23-\x2B\x2D-\x5B\x5D-\x7E]{1,200}$, at most 50 items | yes |
service.customProvider.scopeSeparator | one of " ", , | no |
service.customProvider.pkce | one of S256, none | yes |
service.customProvider.clientAuth | one of client_secret_post, client_secret_basic, none | yes |
service.customProvider.authorizeParams | map of keys (string matching ^[A-Za-z0-9_.-]{1,64}$) to values (string, at most 200 characters) | no |
service.customProvider.hosts | array of string, 1 to 16 items | yes |
service.customProvider.injection | object, one of 2 shapes | no |
service.customProvider.injection.in | header or query | yes |
service.customProvider.injection.name | string matching ^[A-Za-z0-9-]{1,64}$ or string matching ^[A-Za-z0-9_.-]{1,64}$ | yes |
service.customProvider.injection.format | string, 1 to 200 characters | in its shape |
service.customProvider.injection.field | access_token | in its shape |
tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 16 items | no |
reason | string, 1 to 500 characters | no |
Answer (201).
| Field | Type | Required |
|---|---|---|
intake | string matching ^int_[A-Za-z0-9_-]{1,60}$ | yes |
sealKey | object | yes |
sealKey.jwk | object | yes |
sealKey.jwk.kty | EC | yes |
sealKey.jwk.crv | P-256 | yes |
sealKey.jwk.x | string | yes |
sealKey.jwk.y | string | yes |
sealKey.kid | string | yes |
fields | array of object | yes |
fields[].name | string matching ^[a-z][a-z0-9_]{0,63}$ | yes |
fields[].label | string | yes |
fields[].secret | boolean | yes |
fields[].required | boolean | yes |
fields[].multiline | boolean | yes |
fields[].hint | string, or null | yes |
binding | object | yes |
binding.hosts | array of string | yes |
binding.known | boolean | yes |
rotates | boolean | yes |
setup | array of object, or null | yes |
setup[].title | string, 1 to 80 characters | yes |
setup[].body | string, 1 to 400 characters | yes |
setup[].consoleUrl | URL | no |
expiresAt | time (ISO 8601) | yes |
Its own refusals, beyond the common ones: conflict, quota_exceeded, frozen.
service is one of three shapes. { template } names a catalogue template: a key's, or a provider's app template such as google-oauth-client, whose answer carries the template's set-up steps in setup, each body possibly holding {callback} for the platform's page to replace with its own callback URL. { host } is a key for one API host with no template; every request with it is held unless the platform's customReads lets reads through. { customProvider } is the user's own OAuth app with its own provider definition: authorizeUrl and tokenUrl are https on a public host name, with no user name, password or fragment, and never an IP address or a private name; authorizeParams holds at most 8 names, none that Mehen sets itself; each scope and host appears once. The shape allows clientAuth: "none", a public app with no secret, but the vault does not support one yet and refuses its intake with bad_request. The definition is fixed with the app, and the app's client secret is bound to the token URL's host and path, by POST, so it is sent nowhere else. A template outside the platform's templates default is bad_request. A slug the user already has is a rotation of that credential: of another kind or service it is conflict, and so is a different definition under an existing app's name.
intakes.submit#
POST /v1/platform/subjects/:subject/intakes/:intake/submitSubmits the sealed values for an open request. Each box opens only for this request and field, and only once. The name, where the key may go, its tags and its home are the request's, never the body's.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
intake | string matching ^int_[A-Za-z0-9_-]{1,60}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
sealed | map of keys (string matching ^[a-z][a-z0-9_]{0,63}$) to values (object) | yes |
sealed.<name>.v | 1 | yes |
sealed.<name>.epk | object | yes |
sealed.<name>.epk.kty | EC | yes |
sealed.<name>.epk.crv | P-256 | yes |
sealed.<name>.epk.x | string matching ^[A-Za-z0-9_-]{43}$ | yes |
sealed.<name>.epk.y | string matching ^[A-Za-z0-9_-]{43}$ | yes |
sealed.<name>.iv | string matching ^[A-Za-z0-9_-]{16}$ | yes |
sealed.<name>.ct | string matching ^[A-Za-z0-9_-]+$ | yes |
publicFields | map of keys (string matching ^[a-z][a-z0-9_]{0,63}$) to values (string, at most 4,096 characters) | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
item | object | yes |
item.slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
item.kind | one of api-key, bearer-token, basic-auth, custom-headers, oauth-client, oauth-connection, google-service-account, github-app, hmac-key, ssh-key, password-login, totp, database-url, smtp-imap, cloud-access-key, file, note, generic | yes |
item.provider | string, or null | yes |
item.account | string, or null | yes |
item.accountVerified | boolean | yes |
item.tags | array of string | yes |
item.ceiling | one of relay, sign, mint, placeholder, lease, reveal | yes |
item.hosts | array of string | yes |
item.status | one of active, needs_reauth, refresh_retrying, disabled | yes |
item.review | object | no |
item.review.reason | string | yes |
item.review.since | string | yes |
item.updatedAt | string | yes |
item.lastUsedAt | string, or null | no |
item.policy | object | no |
item.policy.default | one of allow, approve, deny | yes |
item.policy.rules | array of object, at most 64 items | yes |
item.policy.rules[].action | one of allow, approve, deny | yes |
item.policy.rules[].methods | array of string, 1 to 16 characters, at most 16 items | no |
item.policy.rules[].paths | array of string, at most 256 characters, at most 32 items | no |
item.policy.rules[].body | one of graphql-mutation, graphql-query | no |
item.policy.rules[].note | string, at most 200 characters | no |
Its own refusals, beyond the common ones: conflict, quota_exceeded, frozen.
Seal each field the intake marked secret under intake/<intake>/<field>, and send the others, such as an OAuth app's client_id, in publicFields.
intakes.cancel#
POST /v1/platform/subjects/:subject/intakes/:intake/cancelCancels an open request for a credential.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
intake | string matching ^int_[A-Za-z0-9_-]{1,60}$ | yes |
Body. {}
Answer (200). {}
Its own refusals: none beyond the common ones.
connects.start#
POST /v1/platform/subjects/:subject/connectsStarts connecting an account with the person's own OAuth app, held in their workspace, and answers the provider's authorisation URL, carrying Mehen's state, the platform's callback and a PKCE challenge where the provider supports one, for the platform to send the person to. Without scopes it asks for the platform's default scopes for a catalogue provider, else the catalogue's, or every scope of the app's own provider definition. It lives 15 minutes.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
provider | string, 1 to 64 characters | no |
client | string matching ^[a-z][a-z0-9-]{1,63}$ | no |
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
redirectUri | URL, at most 2048 characters | yes |
scopes | array of string, 1 to 200 characters, 1 to 50 items | no |
expect | string, 1 to 320 characters | no |
tags | array of string matching ^[a-z0-9][a-z0-9:_-]{0,39}$, at most 16 items | no |
Answer (201).
| Field | Type | Required |
|---|---|---|
connect | string matching ^con_[A-Za-z0-9_-]{1,60}$ | yes |
authorizeUrl | URL | yes |
expiresAt | time (ISO 8601) | yes |
Its own refusals, beyond the common ones: conflict, quota_exceeded, frozen.
Name a catalogue provider, and optionally the client, the user's app for it (by default the app named after the provider's app template, such as google-oauth-client); or name only the client, an app with its own provider definition. redirectUri is the platform's own callback, exactly as the user registered it in their app. A provider outside the platform's providers default is bad_request. An existing connection's slug is a reconnect: for a catalogue provider, pinned to the same account at the provider; for an app with its own definition, through the same app. A device's own POST /v1/connect is refused with forbidden in a managed workspace: connections start here.
connects.finish#
POST /v1/platform/subjects/:subject/connects/finishFinishes a connection with what the platform's callback received. Mehen exchanges the code with the app's client secret, which only Mehen holds, and its own PKCE verifier where it sent a challenge, so the code is useless to anyone else. A state that is not this subject's is not found; a state already used or expired is a conflict.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
state | string, 16 to 256 characters | yes |
code | string, 1 to 4,096 characters | no |
error | string, 1 to 256 characters | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
connect | string matching ^con_[A-Za-z0-9_-]{1,60}$ | yes |
status | completed or failed | yes |
item | object | in its shape |
item.slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
item.kind | one of api-key, bearer-token, basic-auth, custom-headers, oauth-client, oauth-connection, google-service-account, github-app, hmac-key, ssh-key, password-login, totp, database-url, smtp-imap, cloud-access-key, file, note, generic | yes |
item.provider | string, or null | yes |
item.account | string, or null | yes |
item.accountVerified | boolean | yes |
item.tags | array of string | yes |
item.ceiling | one of relay, sign, mint, placeholder, lease, reveal | yes |
item.hosts | array of string | yes |
item.status | one of active, needs_reauth, refresh_retrying, disabled | yes |
item.review | object | no |
item.review.reason | string | yes |
item.review.since | string | yes |
item.updatedAt | string | yes |
item.lastUsedAt | string, or null | no |
item.policy | object | no |
item.policy.default | one of allow, approve, deny | yes |
item.policy.rules | array of object, at most 64 items | yes |
item.policy.rules[].action | one of allow, approve, deny | yes |
item.policy.rules[].methods | array of string, 1 to 16 characters, at most 16 items | no |
item.policy.rules[].paths | array of string, at most 256 characters, at most 32 items | no |
item.policy.rules[].body | one of graphql-mutation, graphql-query | no |
item.policy.rules[].note | string, at most 200 characters | no |
reason | one of denied, provider, mismatch, wider | in its shape |
Its own refusals, beyond the common ones: conflict, quota_exceeded, upstream_failed, frozen.
Send the state and either the code or the provider's error from the callback, never both. Name the subject who is signed in to the platform at the callback, not the one the platform expects. A completed connection made with a catalogue provider names the account the provider verified; one made with an app's own definition names none.
connects.get#
GET /v1/platform/subjects/:subject/connects/:connectReads a connection's progress and outcome.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
connect | string matching ^con_[A-Za-z0-9_-]{1,60}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
connect | string matching ^con_[A-Za-z0-9_-]{1,60}$ | yes |
status | one of open, completed, mismatch, failed, expired | yes |
item | object | no |
item.slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
item.kind | one of api-key, bearer-token, basic-auth, custom-headers, oauth-client, oauth-connection, google-service-account, github-app, hmac-key, ssh-key, password-login, totp, database-url, smtp-imap, cloud-access-key, file, note, generic | yes |
item.provider | string, or null | yes |
item.account | string, or null | yes |
item.accountVerified | boolean | yes |
item.tags | array of string | yes |
item.ceiling | one of relay, sign, mint, placeholder, lease, reveal | yes |
item.hosts | array of string | yes |
item.status | one of active, needs_reauth, refresh_retrying, disabled | yes |
item.review | object | no |
item.review.reason | string | yes |
item.review.since | string | yes |
item.updatedAt | string | yes |
item.lastUsedAt | string, or null | no |
item.policy | object | no |
item.policy.default | one of allow, approve, deny | yes |
item.policy.rules | array of object, at most 64 items | yes |
item.policy.rules[].action | one of allow, approve, deny | yes |
item.policy.rules[].methods | array of string, 1 to 16 characters, at most 16 items | no |
item.policy.rules[].paths | array of string, at most 256 characters, at most 32 items | no |
item.policy.rules[].body | one of graphql-mutation, graphql-query | no |
item.policy.rules[].note | string, at most 200 characters | no |
account | string | no |
grantedScopes | array of string | no |
message | string | no |
expiresAt | time (ISO 8601) | yes |
Its own refusals: none beyond the common ones.
connects.cancel#
POST /v1/platform/subjects/:subject/connects/:connect/cancelCancels a connection that has not finished.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
connect | string matching ^con_[A-Za-z0-9_-]{1,60}$ | yes |
Body. {}
Answer (200). {}
Its own refusals: none beyond the common ones.
approvals.list#
GET /v1/platform/subjects/:subject/approvalsLists the subject's held requests, filtered by status.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Query. Every value is a string on the wire.
| Parameter | Type | Required |
|---|---|---|
status | one of pending, approved, denied, expired, used, ended, revoked, stale | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
approvals | array of object | yes |
approvals[].approval | string matching ^apr_[A-Za-z0-9_-]{1,60}$ | yes |
approvals[].status | one of pending, approved, denied, expired, used, ended, revoked, stale | yes |
approvals[].digest | string | yes |
approvals[].device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
approvals[].workload | object, or null | yes |
approvals[].workload.id | string matching ^wl_[A-Za-z0-9_-]{8,64}$ | yes |
approvals[].workload.label | string | yes |
approvals[].item | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
approvals[].request | object | yes |
approvals[].request.method | string | yes |
approvals[].request.url | string | yes |
approvals[].request.contentType | string, or null | yes |
approvals[].request.contentEncoding | string, or null | yes |
approvals[].request.headersSha256 | string | yes |
approvals[].request.bodySha256 | string | yes |
approvals[].title | string | yes |
approvals[].warnings | array of string | yes |
approvals[].constraints | object | yes |
approvals[].constraints.uses | integer, at least 1, or null | yes |
approvals[].constraints.ttlSeconds | integer, at least 1 | yes |
approvals[].expiresAt | time (ISO 8601) | yes |
approvals[].decidedBy | object, or null | yes |
approvals[].decidedBy.platform | string matching ^plt_[A-Za-z0-9_-]{4,64}$ | yes |
approvals[].decidedBy.key | string matching ^pky_[A-Za-z0-9_-]{4,64}$ | yes |
approvals[].decidedBy.ref | string matching ^[A-Za-z0-9_.:-]{1,64}$, or null | yes |
Its own refusals: none beyond the common ones.
approvals.get#
GET /v1/platform/subjects/:subject/approvals/:approvalReads one held request: the exact request it would send, its digest, the device and workload that asked, what Mehen could not read, and who decided it.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
approval | string matching ^apr_[A-Za-z0-9_-]{1,60}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
approval | string matching ^apr_[A-Za-z0-9_-]{1,60}$ | yes |
status | one of pending, approved, denied, expired, used, ended, revoked, stale | yes |
digest | string | yes |
device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
workload | object, or null | yes |
workload.id | string matching ^wl_[A-Za-z0-9_-]{8,64}$ | yes |
workload.label | string | yes |
item | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
request | object | yes |
request.method | string | yes |
request.url | string | yes |
request.contentType | string, or null | yes |
request.contentEncoding | string, or null | yes |
request.headersSha256 | string | yes |
request.bodySha256 | string | yes |
title | string | yes |
warnings | array of string | yes |
constraints | object | yes |
constraints.uses | integer, at least 1, or null | yes |
constraints.ttlSeconds | integer, at least 1 | yes |
expiresAt | time (ISO 8601) | yes |
decidedBy | object, or null | yes |
decidedBy.platform | string matching ^plt_[A-Za-z0-9_-]{4,64}$ | yes |
decidedBy.key | string matching ^pky_[A-Za-z0-9_-]{4,64}$ | yes |
decidedBy.ref | string matching ^[A-Za-z0-9_.:-]{1,64}$, or null | yes |
Its own refusals: none beyond the common ones.
approvals.decide#
POST /v1/platform/subjects/:subject/approvals/:approval/decideApproves or denies a held request, naming its digest. An approval is good for one use of that exact request by the workload that asked, for at most the platform's decision limit (15 minutes at most). A digest that is not the request's, or a request no longer pending, is a conflict: the request changed, ask again.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
approval | string matching ^apr_[A-Za-z0-9_-]{1,60}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
decision | one of approve, deny | yes |
digest | string matching ^[A-Za-z0-9_-]{43}$ | yes |
ttlSeconds | integer, 60 to 900 | no |
ref | string matching ^[A-Za-z0-9_.:-]{1,64}$ | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
approval | string matching ^apr_[A-Za-z0-9_-]{1,60}$ | yes |
status | one of pending, approved, denied, expired, used, ended, revoked, stale | yes |
digest | string | yes |
device | string matching ^dev_[A-Za-z0-9_-]{1,60}$ | yes |
workload | object, or null | yes |
workload.id | string matching ^wl_[A-Za-z0-9_-]{8,64}$ | yes |
workload.label | string | yes |
item | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
request | object | yes |
request.method | string | yes |
request.url | string | yes |
request.contentType | string, or null | yes |
request.contentEncoding | string, or null | yes |
request.headersSha256 | string | yes |
request.bodySha256 | string | yes |
title | string | yes |
warnings | array of string | yes |
constraints | object | yes |
constraints.uses | integer, at least 1, or null | yes |
constraints.ttlSeconds | integer, at least 1 | yes |
expiresAt | time (ISO 8601) | yes |
decidedBy | object, or null | yes |
decidedBy.platform | string matching ^plt_[A-Za-z0-9_-]{4,64}$ | yes |
decidedBy.key | string matching ^pky_[A-Za-z0-9_-]{4,64}$ | yes |
decidedBy.ref | string matching ^[A-Za-z0-9_.:-]{1,64}$, or null | yes |
Its own refusals, beyond the common ones: conflict, frozen.
projects.create#
POST /v1/platform/subjects/:subject/projectsCreates a project, a named set of settings and keys for computers, with its environments. Every environment is unprotected. The same name again, with the same environments and default environment, answers the existing project with 200; the same name with other environments or another default environment is a conflict.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
environments | array of object, 1 to 16 items | yes |
environments[].name | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
defaultEnv | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
Answer (200 or 201).
| Field | Type | Required |
|---|---|---|
id | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
defaultEnv | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
environments | array of object | yes |
environments[].name | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars | array of object | yes |
vars[].name | string matching ^[A-Za-z_][A-Za-z0-9_]{0,127}$ | yes |
vars[].env | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars[].source | one of plain, item, unset | yes |
vars[].value | string | no |
vars[].item | string matching ^[a-z][a-z0-9-]{1,63}$ | no |
vars[].field | string, or null | no |
Its own refusals, beyond the common ones: conflict, quota_exceeded, frozen.
projects.get#
GET /v1/platform/subjects/:subject/projects/:projectReads a project and its variables, plain values included.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
project | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
Answer (200).
| Field | Type | Required |
|---|---|---|
id | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
defaultEnv | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
environments | array of object | yes |
environments[].name | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars | array of object | yes |
vars[].name | string matching ^[A-Za-z_][A-Za-z0-9_]{0,127}$ | yes |
vars[].env | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars[].source | one of plain, item, unset | yes |
vars[].value | string | no |
vars[].item | string matching ^[a-z][a-z0-9-]{1,63}$ | no |
vars[].field | string, or null | no |
Its own refusals: none beyond the common ones.
projects.setVar#
PUT /v1/platform/subjects/:subject/projects/:project/vars/:nameSets one variable in one environment to a plain value or an item's field, or unsets it when neither is sent. before is the value last read; a stale one is a conflict. A plain value that looks like a secret is refused.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
project | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
name | string matching ^[A-Za-z_][A-Za-z0-9_]{0,127}$ | yes |
Body.
| Field | Type | Required |
|---|---|---|
env | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
before | object, one of 2 shapes, or null | yes |
before.plain | string, at most 4,096 characters | in its shape |
before.item | string matching ^[a-z][a-z0-9-]{1,63}$ | in its shape |
before.field | string matching ^[a-z][a-z0-9_]{0,63}$, or null | in its shape |
plain | string, at most 4,096 characters | no |
item | string matching ^[a-z][a-z0-9-]{1,63}$ | no |
field | string matching ^[a-z][a-z0-9_]{0,63}$ | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
id | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
slug | string matching ^[a-z][a-z0-9-]{1,63}$ | yes |
defaultEnv | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
environments | array of object | yes |
environments[].name | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars | array of object | yes |
vars[].name | string matching ^[A-Za-z_][A-Za-z0-9_]{0,127}$ | yes |
vars[].env | string matching ^[a-z][a-z0-9-]{0,31}$ | yes |
vars[].source | one of plain, item, unset | yes |
vars[].value | string | no |
vars[].item | string matching ^[a-z][a-z0-9-]{1,63}$ | no |
vars[].field | string, or null | no |
Its own refusals, beyond the common ones: conflict, frozen.
projects.remove#
DELETE /v1/platform/subjects/:subject/projects/:projectDeletes a project. Its runs and their placeholders end.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
project | string matching ^prj_[A-Za-z0-9_-]{1,60}$ | yes |
Body. {}
Answer (200).
| Field | Type | Required |
|---|---|---|
deleted | true | yes |
Its own refusals, beyond the common ones: frozen.
audit.read#
GET /v1/platform/subjects/:subject/auditReads the subject's audit chain a page at a time, every row, with its head and whether it verified. Every row names the platform.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Query. Every value is a string on the wire.
| Parameter | Type | Required |
|---|---|---|
cursor | string matching ^[1-9][0-9]*$ | no |
limit | integer, 1 to 200 | no |
Answer (200).
| Field | Type | Required |
|---|---|---|
events | array of object | yes |
events[].seq | integer, at least 1 | yes |
events[].at | integer | yes |
events[].actor | string | yes |
events[].action | string | yes |
events[].target | string, or null | yes |
events[].detail | string | yes |
events[].prev | string | yes |
events[].hash | string | yes |
verified | boolean | yes |
next | string matching ^[1-9][0-9]*$, or null | yes |
filtered | boolean | yes |
head | object, or null | yes |
head.seq | integer, at least 1 | yes |
head.hash | string | yes |
Its own refusals: none beyond the common ones.
workspace.freeze#
POST /v1/platform/subjects/:subject/freezeFreezes the subject's workspace: nothing is used or changed until it is unfrozen.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body. {}
Answer (200).
| Field | Type | Required |
|---|---|---|
frozen | true | yes |
Its own refusals: none beyond the common ones.
workspace.unfreeze#
POST /v1/platform/subjects/:subject/unfreezeUnfreezes the subject's workspace.
Path.
| Parameter | Type | Required |
|---|---|---|
subject | string matching ^[A-Za-z0-9_-]{8,128}$ | yes |
Body. {}
Answer (200).
| Field | Type | Required |
|---|---|---|
frozen | false | yes |
Its own refusals: none beyond the common ones.