How an application connects
Steepworks records each application a customer owns as an Application inside their Account. The record links the projects that build and improve it, its repository, its deployments and one Operations Project that receives requests. People work on those projects as members. The application itself holds a separate machine credential and is never a member, assignee or approver.
| Plane | Who runs it | What your application does there |
|---|---|---|
| Control | Steepworks | Reads its build and operations work, files requests, follows status and, when enabled, discovers the capabilities granted to it |
| Identity | Steepkey | Signs its own users in; Steepworks never proxies application sign-in |
| Runtime | The application and the engines it uses | Calls Merrily, Colter or Didist directly with credentials issued by those services; business authorization stays in the application |
Steepworks is the connection of record, not the runtime path. Your application's own database stays the system of record for its business, and Steepworks keeps the development and operating relationship, releases, costs and bounded evidence.
Getting a credential
There is no self-serve registration. An Account admin issues the credential from the application's Connections panel in the Steepworks workspace, or a Steepworks operator issues it through the same provisioning authority. Every path runs the same Account authorization and shows the raw token exactly once.
- The admin chooses the Application and the scopes it needs from the table below.
- Steepworks returns a bearer token once. Move it to the application's secret storage and never place it in source, configuration committed to a repository, a URL or a log.
-
For request intake the admin also binds an Operations Project. Until that binding
exists, creating a request answers
503 integration_not_configured. - To rotate, issue a replacement, verify it, then revoke the old token. A lost token is revoked and replaced; Steepworks stores only a digest and cannot recover it.
Scopes
| Scope | Grants |
|---|---|
work:read |
Read the work projection for this Application's projects |
requests:read |
List and read the requests this Application has filed |
requests:write |
File requests and post requester replies |
A scope is necessary but never sufficient. Every request rechecks that the token is active, that the Application still belongs to the Account and, for intake, that the bound Operations Project still exists. Revocation takes effect on the next request.
Authorization: Bearer <application token>
Content-Type: application/json
Base URL https://api.steepworks.com/api/v1. These routes carry no browser
session or CSRF state. An Application token cannot call the user REST routes, the
workspace commands or the human MCP endpoint.
Work projection
GET /api/v1/application/work
Returns steepworks.application_work.v2: the Application's active projects,
newest first and at most fifty, each with its goal, milestones and the items on its
board. It is a read-only, observer-level view. Nothing in it can be changed through this
route.
-
Project:
id,key,name,status,starts_on,due_on,updated_at,goal,milestones, lane counts and alinks.steepworksURL into the project room. -
Milestone:
title,status,builder_status, dates,outcomeandaccepted_at. -
Item:
kind,ref,title,status,verification_state,confirmation_required, risk, priority and impact, milestone and workstream labels,due_on, the accountable person's label, the performer's label and a deep link.
Labels are display names, never identifiers or email addresses. A status in this projection is Steepworks' record of the work. It is not customer acceptance and not evidence that a release happened.
Requests API
Your application's own users ask for things: a fix, a feature, a question. The Requests API carries each one into the bound Operations Project, where the Steepworks team triages it, promotes it to work and resolves it. Your application polls the public status back and shows it to the requester. Steepworks keeps triage, acceptance, promotion and delivery status; the application keeps the conversation with its user.
| Method and path | Scope | Purpose |
|---|---|---|
POST /application/requests |
requests:write |
File a request; 201 on first delivery, 200 on replay
|
GET /application/requests |
requests:read |
List this Application's requests, optionally filtered by
?requester_ref=
|
GET /application/requests/:id |
requests:read |
Read one request with its public status, note and comments |
POST /application/requests/:id/comments |
requests:write |
Post a reply from the original requester |
Filing a request
Send an Idempotency-Key
header equal to the body's external_key. Make it a stable key from your own record, 8 to 300
characters of letters, digits, :, ., _
and -, starting with a letter or digit. Resending the same key and body after
a timeout returns the original request; a different body under the same key is a
conflict.
POST /api/v1/application/requests
Idempotency-Key: yourapp:operator-request:7f3c…
{
"schema": "steepworks.application_request.create.v1",
"external_key": "yourapp:operator-request:7f3c…",
"requester": {"ref": "user:1042", "label": "Front desk"},
"request": {
"title": "Recall letters skip patients without email",
"body": "Steps to reproduce and what we expected…",
"kind": "issue",
"workstream": "reliability",
"source_path": "/console/requests"
},
"occurred_at": "2026-10-27T15:04:05Z"
}
-
requester.refis your stable identifier for the person asking, 3 to 300 characters;labelis an optional display name up to 200 characters. Later replies must carry the sameref. -
request.titleis 2 to 400 characters;bodyis optional Markdown up to 20,000 characters. -
kindis one ofrequest,question,discussion,issueorfeature; it defaults torequest. -
workstreamis one ofproduct_experience,reliability,content_data,accessibilityorquestion; it defaults toproduct_experience. -
source_pathis an optional relative path inside your application where the request came from. Absolute URLs are rejected. -
occurred_atis an optional ISO-8601 timestamp of when the user asked.
The response wraps the request projection, steepworks.application_request.v1: id, external_key, requester, the
request
block you sent, public_status, public_note, comments, created_at, updated_at,
completed_at
and completion_url,
with meta.replayed
telling you whether this was a replay.
Replying on behalf of the requester
POST /api/v1/application/requests/:id/comments
Idempotency-Key: yourapp:operator-reply:9b21…
{
"schema": "steepworks.application_request.comment.v1",
"external_key": "yourapp:operator-reply:9b21…",
"requester": {"ref": "user:1042", "label": "Front desk"},
"body": "Also affects the Tuesday batch."
}
The body is 1 to 10,000 characters. A reply whose requester.ref differs
from the request's original requester answers 403 requester_mismatch.
Comments from the Steepworks side arrive in the request's comments list
on your next read.
Request statuses
public_status is a bounded vocabulary your application can render
directly. It follows triage first, then the promoted task.
| Status | Meaning |
|---|---|
reviewing |
Received; the team has not decided yet |
needs_details |
The team asked the requester for more; read public_note |
planned |
Promoted to work that is proposed or ready |
in_progress |
Work has started, or is blocked while it stays in progress |
final_checks |
The work is in review |
completed |
Resolved or done; completed_at
and completion_url
are set
|
deferred |
Kept, but not scheduled |
not_planned |
Declined or canceled, with the reason in public_note |
Poll rather than wait for a callback. Steepworks does not yet deliver signed events to
applications, so a request your application has already created should be re-read with GET, never re-created. Keep the last known status if a read fails.
Errors
Every error is {"error": {"code": "…", "message": "…"}}.
| HTTP | Code | Cause |
|---|---|---|
| 400 | invalid_idempotency_key |
Header missing, repeated or outside the allowed pattern |
| 400 | idempotency_key_mismatch |
external_key differs from the header |
| 401 | unauthorized |
Missing, revoked or unknown bearer token |
| 403 | forbidden |
The token lacks the scope for this route |
| 403 | requester_mismatch |
A reply from someone other than the original requester |
| 404 | not_found |
No such request for this Application |
| 409 | idempotency_conflict |
Same key, different content |
| 422 |
unsupported_schema, invalid_source_path,
invalid_request
|
Wrong schema name, absolute source path or a field outside its bounds |
| 503 | integration_not_configured |
No Operations Project is bound for intake |
Capability API
Planned. Implemented and tested in the repository, disabled by default and not enabled for any customer. The contract below is what will be switched on, not an endpoint you can call today.
Beyond build work, Steepworks will let an application discover which shared services its Account has granted it and read bounded, source-backed facts from them. The first capabilities are a connection read and two reads from a linked Merrily customer workspace. Neither communicates with a customer, approves an action, charges usage or runs a workflow.
Setup chain
An Account admin or a Steepworks operator provisions the chain in order, and each step records a version: register the application privately within the Account, install it for one environment, grant each capability, bind each resource (for Merrily, only after the reviewed two-sided account link is accepted), activate the installation and issue a credential. Suspension blocks reads; disconnect is terminal.
Capability credentials are separate from Application tokens, expire within 90 days and
carry their own scopes: capabilities:read, connection:read,
merrily:readiness
and merrily:context.read. Every request also
names its environment, and installation, header and deployment environment must agree.
Authorization: Bearer <capability token>
X-Steepworks-Environment: production
| Request | Scope | Returns |
|---|---|---|
GET /application/capabilities |
capabilities:read |
steepworks.capabilities.v1: the capabilities this installation may
call, with readiness rechecked on each call
|
GET /application/connection |
connection:read |
steepworks.connection.v1: installation ID, environment, app key,
name, revision and capability version
|
GET /application/merrily/readiness |
merrily:readiness |
steepworks.merrily.readiness.v1: the linked customer and limited
readiness checks
|
GET /application/merrily/contacts/:contact_id/context
|
merrily:context.read |
steepworks.merrily.contact_context.v1: one contact and up to 20
source-backed facts with provenance, marked incomplete where coverage ends
|
None of these routes accepts query parameters, and a supplied Account or installation
ID is rejected rather than trusted. Responses carry Cache-Control: no-store.
Errors use the same envelope as above: 401 unauthorized for an invalid,
expired or inactive identity or a wrong environment, 403 forbidden for a
missing scope, grant or binding, 422 invalid_arguments for unexpected
input and 404 not_found while the API is disabled.
Durable operations
Planned. Switched on separately from the capability API and off by default. The first contract admits only read operations.
An operation is a capability call Steepworks runs for you with a receipt: admitted
under the Account, dispatched by a worker, observed and kept. Your application submits
it, polls the receipt and may cancel before dispatch. It needs
operations:submit
and operations:read, optionally operations:cancel, plus the scope of the underlying read.
| HTTP | Application MCP tool |
|---|---|
POST /application/operations |
steepworks_operations_submit |
GET /application/operations/:id |
steepworks_operations_get |
POST /application/operations/:id/cancel |
steepworks_operations_cancel |
{
"capability": "merrily.readiness",
"contract_version": 1,
"idempotency_key": "readiness-check-001",
"input": {}
}
Submission answers 202
with steepworks.operation.v1: a stable id, status, version, cancellation flag, observation revision, bounded
result or error and UTC timestamps. Polling returns the same shape with 200. Cancellation takes the receipt's expected_version.
Statuses are queued, dispatching, succeeded, failed,
canceled
and uncertain. A successful
readiness read may still report ready: false.
Replay is keyed by Account, installation, environment, capability, contract version
and your key; the same key with different input answers 409 idempotency_conflict. Each installation may hold 100 open operations;
beyond that, 429 rate_limited. Input is bounded to 4 KiB and results to 8
KiB. No usage charge is inferred from an operation.
Application MCP
Planned. Served only when the capability API is enabled.
POST https://api.steepworks.com/api/v1/application/mcp
Agents running inside your application can use the same capability credential over
MCP, with the same bearer token and X-Steepworks-Environment
header as
REST. tools/list
needs capabilities:read
and returns only the
reads this installation is admitted to. tools/call
rechecks scope, grant,
binding and the remote service on every call.
| Tool | Purpose |
|---|---|
steepworks_connection_read |
This installation's identity and environment |
steepworks_merrily_readiness |
Readiness of the linked Merrily workspace, when granted |
steepworks_merrily_contact_context_read |
Bounded context for one contact_id, when granted |
steepworks_operations_submit, steepworks_operations_get,
steepworks_operations_cancel
|
The durable operation protocol above |
This endpoint is distinct from the human MCP endpoint documented for agents. A
capability credential cannot call /api/v1/mcp or any workspace command,
and a user credential cannot call this one.
What the connection never does
- It does not act as a person. No scope lets an application create tasks, accept work, approve spending or release software. A request becomes work only when the Steepworks team promotes it.
- It does not proxy your runtime. Customer records, commerce and hosted agents are called directly on Merrily, Didist and Colter with credentials those services issue.
- It does not hold your users' identity. Sign-in to your application is Steepkey's or your own; Steepworks sees requester labels, not accounts.
-
Its status is a record, not proof. A
completedrequest or adoneitem says Steepworks recorded the result. Acceptance, release and payment are decided by named people and kept separately. - It does not push. There are no signed callbacks yet; your application polls.
What is planned
These are direction, not commitments with dates. Each becomes available only after it is verified end to end and this page says so.
- Enabling the capability API, durable operations and application MCP for a first customer.
- Grants beyond Merrily reads: Didist, Colter and other engine capabilities.
- An OpenAPI description and client libraries for the Application routes.
- A Connections view inside the workspace that shows every active credential and grant.
- Signed event delivery to applications in place of polling.
- Self-serve registration for application developers under an Account admin's approval.
Credentials and security
- Tokens are random, shown once and stored only as a digest. Steepworks cannot read them back; losing one means revoke and replace.
- Supply tokens through the environment or a secret store, never on a command line, in a URL, in a repository file or in a log.
- Account admins list and revoke Application tokens from the application's Connections panel. Revocation takes effect on the next request.
- Capability credentials expire within 90 days; rotate by issuing, verifying, then revoking.
-
Use HTTPS everywhere outside loopback development. Capability routes answer
Cache-Control: no-store. - Application credentials never enter user routes, and user credentials never enter application routes. Keep them in separate secrets with separate owners.
Have an application to connect?
Start with what the application should do for your business. We set up the Account, the Application record and the first credential with you.
Start with a goal