Menu
Open workspace

Resources

API & MCP

Nordform projects can be reached from your own tools. A script, a build pipeline or an AI assistant signs in with a personal token and works inside your account, under permissions you set and a run budget you choose.

There are two ways in and one token for both. The HTTP API is the same interface the workspace itself uses. The MCP server exposes that interface to AI clients such as Claude Code over the Model Context Protocol.

Quick start

You need a Nordform account. Sign in with Google, or read about Nordform for practices if you are evaluating this for a practice. Tokens act as you, so they reach only what your own account reaches. A guest trial session cannot create tokens.

1. Create a token. Open Settings → API & MCP in the workspace. Name the token for the place it will live, pick the permissions it needs, set an expiry and a run budget, and copy the secret. It is shown once, in the form bsk_ followed by 64 characters.

2. Connect an assistant. The token screen offers Copy message for Claude: paste that message into Claude Code or the Claude desktop app and Claude runs the setup, then checks the connection. To do it yourself, in Claude Code:

claude mcp add --transport http buildable https://app.nordform.ai/mcp --header "Authorization: Bearer bsk_your_token_here"

Setup for Cursor, VS Code, Codex and the Claude app, with browser sign-in or a token, is under Setting up your assistant.

3. Or call the API directly.

curl -H "Authorization: Bearer bsk_your_token_here" https://app.nordform.ai/api/projects

Authentication and permissions

Send the token as a bearer credential on every request: Authorization: Bearer bsk_…. A client that cannot carry a header can authorize in the browser instead — see Connecting without a token below — and receives a credential of the same shape. When a bearer token is present, browser cookies are ignored, so a token never inherits a signed-in session's wider rights.

Each token carries permissions chosen when it is created, and they cannot be widened afterwards:

Permission What it allows Applies to
read List and open projects, revisions, jobs, artifacts, reviews, documents, usage and library part data GET requests
comment Write, reply to, resolve and correct comments on your projects and reviews. Changes no design, file or decision Comment routes only
write Create and archive projects, cancel a job, upload and remove documents, write review comments and decisions, write library part data Other writes
run Start a check, a generation or a render POST /api/jobs and POST /api/renders
admin Company membership, teams, invitations, workflow connectors and webhook destinations Everything under /api/companies, /api/company-invites, /api/company-connectors, /api/review-invites and /api/webhooks

write includes comment. Give an assistant read and comment when it should review and leave notes but never change a design.

Administration is scoped as a whole rather than by method: listing a company's members is as much an administrative act as changing them, so a GET there needs admin too. Give a token admin only when it administers the account.

A token also carries a run budget: a lifetime ceiling on how many runs it may start. When it is used up the token still reads and writes, but cannot start further runs. A start the API rejects does not count against it.

It also carries a request limit: how many requests it may make in any sixty-second window, from 1 to 600 and 120 by default. Going over it answers 429 with Retry-After: 60 and reaches nothing. The limit is per token.

Your account's own limits still apply on top: your credit balance, active and daily job caps, and storage quotas are unchanged by which token was used.

Tokens cannot be managed with a token: a credential may not mint or read credentials, so that stays in the workspace. A token can hold at most 90 days by default and 365 at most, up to 20 active tokens per account.

What a token did

Every request a token makes is recorded: the time, the method, the path, the status we answered with, and for MCP the tools the request carried. Query strings and bodies are never recorded, so a credential in a URL cannot end up in the log. Open the token in the workspace → API & MCP access and choose History to read the last 200 requests. Records are kept for 30 days.

Revoking a token takes effect immediately.

Watching an assistant work

When an assistant reads or changes one of your own projects through a token or a browser sign-in, anyone with that project open in Nordform sees it among the people there, marked as an agent and named by the token's name or the client you approved. It stays listed for 45 seconds after its last request, and disappears at once when you revoke the token.

When it saves a new version, your open window says which agent saved it; in the workspace that version opens as it lands if you have no change of your own open, and otherwise waits for you to load it. The token itself is never shown. An agent appears only on your personal projects, not on company designs or in the project list, and it shows the saved versions, not each step of a change in progress.

Connecting without a token

Clients that support browser sign-in do not need a pasted token. Point one at https://app.nordform.ai/mcp and it will discover this workspace, register itself, and send you to Nordform to approve the connection. You sign in as yourself, see exactly which permissions are being asked for, and choose. The client then receives its own credential, valid for an hour and renewed in the background for as long as the connection lasts.

Nordform implements the standard pieces: protected resource metadata at /.well-known/oauth-protected-resource, authorization server metadata at /.well-known/oauth-authorization-server, dynamic client registration at /oauth/register, and the authorization code flow with PKCE (S256 only) at /oauth/authorize and /oauth/token. Clients are public, so no client secret is issued or accepted.

A browser-approved connection can hold read, write and run, but never admin: account administration is granted only to a token you create yourself in the workspace. Refreshing does not renew the run budget; the whole connection shares one.

Connections appear in the token table in the workspace alongside your own tokens, marked as connected apps, and end the moment you revoke them.

Setting up your assistant

Every client below can either sign in through the browser, where you approve the connection in Nordform and paste nothing, or send a token you created under Settings → API & MCP. Signing in is the better choice where a client offers it: the connection is listed in your token table and you can end it there. Where a token is used, keep it in the client's secret store or an environment variable, never in a file you commit. These steps follow each client's own documentation as it stood on 8 October 2026; if a menu has moved since, the client's documentation is the authority.

Claude Code. Sign in:

claude mcp add --transport http buildable https://app.nordform.ai/mcp

then run /mcp in a session and choose to authenticate. With a token instead:

claude mcp add --transport http buildable https://app.nordform.ai/mcp --header "Authorization: Bearer bsk_your_token_here"

The Claude app (web and desktop). Open Customize → Connectors, choose Add, then Add custom connector. Give it a name and the address https://app.nordform.ai/mcp, continue, and choose to sign in. On a Team or Enterprise plan an owner adds the connector under Organization settings → Connectors first, and each member then connects it as themselves. Claude's request-header option would share one token among everyone who uses the connector, so use sign-in there. The app connects from Anthropic's servers, not from your computer.

Cursor. In .cursor/mcp.json in a project, or ~/.cursor/mcp.json for every project, an entry with only the address signs in through the browser:

{
  "mcpServers": {
    "buildable": { "url": "https://app.nordform.ai/mcp" }
  }
}

With a token, add "headers": { "Authorization": "Bearer ${env:BUILDABLE_TOKEN}" } to the entry and set BUILDABLE_TOKEN in your environment.

VS Code with GitHub Copilot. In .vscode/mcp.json, this asks for the token once and keeps it in VS Code's secret storage:

{
  "inputs": [
    { "type": "promptString", "id": "buildable-token", "description": "Nordform API token", "password": true }
  ],
  "servers": {
    "buildable": {
      "type": "http",
      "url": "https://app.nordform.ai/mcp",
      "headers": { "Authorization": "Bearer ${input:buildable-token}" }
    }
  }
}

Codex. In ~/.codex/config.toml:

[mcp_servers.buildable]
url = "https://app.nordform.ai/mcp"

then run codex mcp login buildable to sign in. With a token instead, add bearer_token_env_var = "BUILDABLE_TOKEN" and set that variable. The same file serves the Codex command line, its editor extension and the ChatGPT desktop app.

Anything else. A client needs three things: the Streamable HTTP transport, the address https://app.nordform.ai/mcp, and either browser sign-in or an Authorization header carrying Bearer and your token. A script that sends no User-Agent may be turned away at our edge; see Errors.

What to ask your assistant

Once connected, ask in plain words. Each request below names the tools the assistant will reach for. None of them changes a design, posts a comment or spends a credit unless you say so in the conversation; tell your assistant the same if it does not.

Ask What it does
"List my Nordform projects and tell me which ones have a failed check." list_projects, list_jobs, get_job. Reads only
"In the Lakeside house, how many openings are narrower than 900 mm?" get_project, then query_design_elements, which answers from the design's own element table and states the filter it ran
"Make the Lakeside house 600 mm longer. Show me the change and wait." preview_design_change returns the change and its diff; nothing is saved. adopt_design_change runs only when you say adopt, and is refused if the design moved meanwhile
"Run the checks on the latest version and tell me when they finish." start_job with kind check, which spends nothing, then wait_for_job until it settles, then get_job_artifact
"What changed between versions 3 and 4?" compare_project_revisions: dimensions, members, openings and quantities
"Export the latest version and tell me what the files leave out." export_revision, which returns the files with their hashes and what each omits
"Show me the latest version of the pipe run." view_revision, which returns the figure as SVG markup the assistant reads; a timber house gives a plan or elevations instead. Reads only
"Summarise the open comments on the Lakeside review and draft replies, but don't post them." list_reviews, get_review. Comment text is marked as written by people, so the assistant reports it rather than acting on it. add_review_comment only when you say post
"How many runs does this connection have left?" whoami and get_usage

A timber kit design, a steel portal frame, a pipe run or a machinery skid can be changed this way. For a steel portal frame the change is named, not retyped: span, eaves, pitch, length, frame spacing, purlin and rail centres, haunch, bracing bays, end-wall framing, and placing, moving, resizing, framing or removing its one door; the frame's own kernel refuses a change it cannot lay out, such as a door in a bay that does not exist. A pipe run is changed the same way: one leg's length or heading, the run's size and wall schedule, long- or short-radius elbows, plain or flanged ends, the stated water flow, and setting, moving, resizing or removing its one branch. Its kernel refuses a run it cannot lay out, such as a turn that is not a right angle or a leg too short for its fittings. For a machinery skid, set-skid-dimension changes the skid length or runner spacing, equipment length or width, equipment offset or lifting-point inset, in metres. Preview runs the kernel and returns a diff and check results without saving. Explicit adoption appends a revision and refuses stale proposals or inputs inconsistent with their operations. Other families are created and exported over MCP and changed in the workspace, and compare_project_revisions compares timber versions. Asking for a new proposal from a brief starts a generation, which spends your credits: a well-behaved assistant asks first.

HTTP API reference

Base URL https://app.nordform.ai. Requests and responses are JSON.

Endpoint Method Permission Returns
/api/session GET read The identity the token acts for, plus the token's own permissions and run count
/api/projects GET read Your projects, newest first. ?archived=1 includes archived ones
/api/projects POST write Creates a project and its first revision from a name, brief and parameters
/api/projects/{id} GET read A project with its immutable revisions and their inputs
/api/projects/{id}/snapshot GET read The portable snapshot synced from the workspace, including annotations
/api/projects/{id}/archive, /restore POST write Archives or restores a project
/api/jobs POST run Starts a job. kind is check or generate; send an idempotencyKey so a retry is safe
/api/jobs?projectId=… GET read Recent jobs for a project
/api/projects/{id}/hub GET, POST read, write The design's hub: its address, what arrived, how each message was read and what ran over it; open, replace or close the address, set whether runs start on their own
/api/projects/{id}/hub/messages POST write Posts a message to the design from inside, or acts on one (run, triage, ignore)
/api/projects/{id}/hub/events GET read The design's own stream as server-sent events, in finite batches resumed by id
/api/projects/{id}/hub/responses GET, POST read, write Whether review comments and change requests on the design go to its hub, and where each one went; switch it off or on ({forward}); …/responses/bcf sends the notes a BCF import added
/api/projects/{id}/hub/answers/{messageId} POST write Sends or dismisses the answer drafted from the record for a question ({action, include}: action is send or dismiss, and include names the quantity comparisons to add). Once per draft, owner only
/api/projects/{id}/actions GET read The last 100 workspace actions on the design's stream: packages issued, sign-offs given, decisions recorded, versions adopted
/api/projects/{id}/graph GET read The design's graph, one step around it: what it reads and what reads it, its delivery interfaces with their state, and its open recalculations. Owner only; reading it writes nothing
/api/projects/{id}/scenarios GET, POST read, write Kept scenarios of the design. POST {jobId, candidateId} keeps a scenario, copied from the run's own result; POST …/scenarios/{scenarioId} with {action}, adopted or dismissed, closes it once and saves no version
/api/kept-scenarios GET read Every kept scenario waiting on you, on your own designs and on company designs you edit or administer
/api/hub/waiting GET read Waiting on you: hub messages held for you, drafted answers, assignments and decisions waiting on you, with one count. Read only
/api/hosts, /api/hosts/{source}/{id} GET read The hosts you may read beyond your own projects, and one version of one, cut to what its share allows
/api/design-links GET, POST read, write Links between designs that you are a side of, with their confirmation state; POST /api/design-links/{dependentId}/confirm, /withdraw or /request answers one as your side allows
/api/company-designs/{companyId}/{designId}/host-shares GET, POST read, write A company design's host shares with other companies: from a version, optionally through one, at a level; POST …/host-shares/{shareId} with {revoke: true} revokes one. Company administrators only
/api/jobs/{id} GET read Job status, artifacts once it succeeds, or the recorded failure reason
/api/jobs/{id}/events GET read Progress events as a finite server-sent-event batch
/api/jobs/{id}/cancel POST write Cancels a queued or running job
/api/artifacts/{id} GET read A succeeded job's JSON artifact
/api/usage, /api/credits GET read Usage counters, storage, and the current credit balance. /api/generations is the earlier name for the credits endpoint and still returns the same body
/api/documents GET read Ready private documents, name and size only
/api/projects/{id}/documents GET, POST read, write A project's documents; POST reserves one from revisionId, clientId, name, sha256 and byteLength
/api/documents/{id}/content PUT, GET write, read Uploads the reserved bytes, or streams them back. The upload is refused unless its length and SHA-256 match the reservation
/api/documents/{id} DELETE write Removes a document and returns its space to your quota
/api/reviews, /api/reviews/{id} GET read Reviews you own or were invited to, with comments and decisions
/api/reviews/{id}/comments POST comment Writes a comment, optionally replying to another or pointing at one frame member or part. On an opening a pipe or cable needs through the structure around it (a part named HR-01 and so on), a new comment may carry verdict: accepted or rejected
/api/reviews/{id}/decisions POST write Records a verified expert's changes_requested or signed_off, pinned to the review's scopeHash
/api/renders POST run Starts an illustrative render from a PNG of the concept view
/api/renders/{id}, /image GET read Render status, or the finished picture
/api/renders/{id} DELETE write Cancels a render and deletes anything stored for it
/api/companies, /api/companies/{id}/… GET, POST, PUT, DELETE admin Companies, members, teams and invitations
/api/companies/{id}/opener/designs GET admin Concepts that began from this company's embedded kit designer: when, which of its kits, the size it was started at, the page it stood on and the context packages carried. It names no person; a design is the maker's to send you.
/api/webhooks, /api/webhooks/{id} GET, POST, DELETE admin Webhook destinations and their delivery history
/api/standards GET read The Standards workspaces of the companies you belong to, with their company id and component count
/api/standards/{id}/parts GET, POST read, write The attribute list with units, and each accepted library component with its part data and where and when each value was last set; POST writes several components' part data in one batch (see below)
/api/standards/{id}/parts/{componentId} GET, PUT read, write One component's part data and every write that changed it; PUT writes part data (see below)

Of a Standards workspace, a token reaches only the workspace list and part data. Sources, components, house rules, audits and decisions stay in the signed-in workspace.

Library part data

A company's purchasing or ERP system can keep part data current on the components in its Standards library. A write is PUT /api/standards/{id}/parts/{componentId} with a body of key, source and values:

{"key":"sap-2026-10-05-0001","source":"SAP MM",
 "values":{"articleNumber":"RR-HEA200","unitPrice":41.5,"currency":"EUR","priceUnit":"m","leadTimeDays":14}}
Attribute Value
articleNumber Text, up to 60 characters
gtin GTIN-8, -12, -13 or -14 (EAN) with a valid check digit
supplierRef Text, up to 80 characters
unitPrice Number, 0 or more, in currency per priceUnit
currency EUR, SEK, NOK or DKK
priceUnit piece, m, m2, kg or t
leadTimeDays Whole days, 0 to 730
availability in-stock, to-order, discontinued or unknown
massPerMetre kg/m, as the seller states it
finish Text, up to 80 characters

Send only the attributes that changed; null clears one. Any other key is refused by name. A component's designation, material, grade and stock lengths are its definition, accepted by an engineer the company names, and cannot be written here; part data is never read by an audit or by a published library version. The key makes a retry safe: the same key with the same write is answered with what it recorded, and the same key with a different write is refused with 409. A write that changes nothing records nothing. Each write is kept with who made it, when, and the source it names, and the history shows each value before and after. A price here is what your system holds, not a quote.

Several components at once are POST /api/standards/{id}/parts with key, source and writes, a list of up to 200 {componentId, values}. The batch is all or nothing: a refused value, a component the library does not hold or a component listed twice refuses the whole batch with each reason, and nothing is written. One key covers the batch and makes a retry safe in the same way. The answer lists the components written, those whose values already matched, and what changed on each. The same batch is what a company administrator or named engineer records from a purchasing spreadsheet in the Standards workspace: a CSV or xlsx with a column naming the component (its id, or a designation only one component carries) and the attributes above by key or label, read in the browser row by row and sent as one batch after review.

The token must belong to a company administrator or a named engineer of the workspace; other members read part data but do not write it.

MCP reference

Endpoint https://app.nordform.ai/mcp. The transport is stateless Streamable HTTP: one JSON-RPC message or batch per POST, a JSON reply, 202 for notifications. A batch carries at most 32 messages, and each message counts as one request against the token's limit. There is no server-initiated stream and no session identifier, so nothing needs to be kept open between calls. Protocol versions 2025-06-18, 2025-11-25 and 2025-03-26 are accepted; anything else is answered with 2025-06-18. The server advertises tools only.

curl -X POST https://app.nordform.ai/mcp \
  -H "Authorization: Bearer bsk_your_token_here" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Tool Permission What it does
whoami — The identity the token acts for, its permissions and remaining run budget
preview_frame — Builds the concept frame for given parameters and returns a member summary, ridge height and cut list. Saves nothing
query_design_elements — Lists the members and openings of the concept frame for given parameters, filtered by kind, role, wall, storey and typed comparisons such as width_mm < 900, and states the filter it ran with the count. A question about the design is answered from this table, not from an assistant's memory. Saves nothing
export_concept_cad — Writes the concept frame for given parameters as DXF, IFC 4.3 or STEP, reads the file back and withholds it if it does not match. Saves nothing
list_context_packages — The context packages a project may name, filtered by construction system or category
list_rules — The published design rules bundled with the engine, by subject or by family: standing, citation and whether a rule decides anything. None decides without a named person's approval
get_rule — One bundled rule in full: our statement of it, its source and clause, the test as data, the facts it reads and its review date
find_duplicate_elements — Reports members drawn twice, members of one section overlapping along a line and zero-length members, with what it did not assess. Saves nothing
list_projects read Your projects
get_project read A project with its revisions and the inputs behind each one
get_project_snapshot read The portable snapshot synced from the workspace
view_revision read A drawing of a saved revision as SVG text (image/svg+xml) in an embedded resource, not a PNG: a registered family's own figure, or a timber house's plan or elevations. Up to 1 MB; concept only
create_project write Creates a project and its first revision
archive_project write Archives or restores a project
start_job run Starts a check or a generation, generating an idempotency key if you omit one
request_flow run Runs a named flow on a saved revision and records it: the kernel, the inputs it read and the outcome. Refuses a flow the revision does not take, and an older revision whose inputs the flow reads have changed since. The access platform flows spend credits like a generation unless every stage is reused from an earlier run; the check flows spend none
list_jobs read Recent jobs for a project
get_job read Status, artifacts or failure reason
get_job_events read Progress events parsed into JSON
wait_for_job read Waits up to 25 seconds for a job to succeed, fail or be cancelled, then returns its status with the progress events since your cursor. Call it again with the returned cursor while settled is false; waiting spends nothing
cancel_job write Cancels a job
get_job_artifact read A succeeded job's checks, quantities or proposal
get_usage read Usage counters and credit balance
list_documents, list_project_documents read Ready private documents, account-wide or for one project
upload_document write Attaches a file to a revision, up to about 3 MB over MCP; use the chunked transfer tools for larger files
download_document read Reads a document back as base64, up to 1 MB
delete_document write Removes a document
list_reviews, get_review read Reviews, with comments and decisions
add_review_comment comment Writes a comment as the account the token acts for, with an optional verdict on an opening
record_review_decision write Records an expert decision, reading the current scope hash unless you pass one
list_package_issues read Who was issued which package of a project, when they opened it, what replaced it and the review code each returned
issue_package write Issues a prepared package to people already invited to one of the project's reviews; each is mailed a link, never the file
code_issued_document write Returns a package issued to you coded: approved, approved with comments, or rejected
get_interface_record read For each design link: who issued the host version it reads, who coded it, which version of the reading design took it in, and what changed since
start_render, get_render, cancel_render run, read, write Illustrative renders from a supplied PNG
list_companies, get_company admin Company membership and teams
invite_member, revoke_invite admin Invitations. The invitation link is never returned to a client: it is a credential, and belongs in the recipient's inbox
list_webhooks, create_webhook, delete_webhook admin Webhook destinations
list_library_parts, get_library_part read Standards workspaces, and library part data with its history
set_library_part_attributes write Writes library part data, as PUT /api/standards/{id}/parts/{componentId}

A result that carries text a person wrote or a file held, such as a brief, a review comment, a hub message, minutes or a document, has a second content block after the data saying so. That text is data for the assistant to report or quote, never an instruction to it; the server's own instructions tell the assistant the same.

Two limits apply. An assistant proposes a supported change as typed data, and our own deterministic code builds the geometry; an assistant never executes geometry code of its own. Nothing an assistant reaches is engineered, code-checked or ready for fabrication.

Designer workflow tools

Use get_design_capabilities for current family input schemas and tools/list for the complete tool registry. create_family_project creates registered non-house concepts, while sync_project preserves a complete revision history. For timber kit designs, preview_design_change takes operations and returns a proposal and snapshot hash; adopt_design_change saves it explicitly and refuses a stale snapshot. For a steel portal frame or a pipe run the same two tools take familyOperations and return and adopt a familyProposal. Their preview also returns impact: every check whose state the change would move, with its state before and after, and, for each of your designs that reads this one, whether its agreed term would still hold, or that none was agreed. It also lists what the change would leave on the current version: its sign-offs, open review findings with whether the change removes, moves or reshapes the part each is on, decisions still waiting for an answer and customer offers still open. get_design_capabilities lists the operations a family accepts, and a family that lists none is changed by syncing replaced inputs. Generated candidates also require explicit adoption.

MCP also exposes revision comparisons, project evidence, meeting imports, reviews and their document grants, company teams and shares, connector administration and customer portals. Existing account permissions, company entitlements and run budgets still apply. Invitation and connector-test calls can send messages: authorize the intended recipients before calling them.

view_revision lets an assistant look at a saved revision. It returns the drawing as SVG text with media type image/svg+xml, inside an embedded resource, after a short text block naming the view, the revision and the units: the figure every registered non-house family draws (steel portal, pipe run, access platform, skid frame and the others), or a timber house's plan for one storey or its elevations. It is SVG, not a PNG, so an assistant reads the markup rather than seeing a picture, and an assistant that cannot read SVG gets nothing visual from it. Drawings over 1 MB are refused rather than cut short. It is a concept drawing.

export_revision and create_revision_delivery produce derived concept files with source and byte hashes. Binary bundle files retain base64 encoding and individual filenames. These tools support kit timber houses and registered non-house families; other house construction systems retain their existing package workflow.

The workflow tools, by area:

  • Family designs and changes: create_family_project, get_design_capabilities, preview_design, preview_design_change, adopt_design_change, adopt_generation_candidate, reject_candidate, compare_project_revisions, sync_project.
  • Why a check fails: explain_check finds the newest earlier version where a family design's check passed and returns each input changed since, one at a time, to see which one alone makes it pass; the others are listed as ruled out, or as untestable when the kernel refuses one on its own, and when no single input explains it, it says whether all of them together do. Read only; it suggests no values, and the check's own recommendations are returned unchanged. Its answer for a failing check, and the impact of a preview, list what they rest on as cites: the revisions, parts, checks, designs and inputs they name, by id, each only where it exists in the design; citesDropped counts any that did not.
  • Designs that read each other: get_design_links (what a design reads and what reads it, with the pinned version and the agreed term) and get_recalculations (the open recalculations around a design). Your own designs only; both read only.
  • Files and evidence: view_revision, export_revision, create_revision_delivery, get_project_evidence, get_project_workflow, list_related_projects.
  • Large documents: reserve_document_upload, upload_document_chunk, complete_document_upload, get_document_transfer, download_document_chunk.
  • Meetings: list_project_meetings, import_project_meeting, manage_meeting_summary.
  • Reviews: create_review, invite_review_participant, revoke_review_invite, add_review_viewpoint, get_review_change_source, get_review_comparison, share_review_comparison, remove_review_comparison, share_review_document, unshare_review_document, list_review_signoffs, get_feedback_resolution, record_feedback_resolution.
  • Shared orders: list_shared_orders lists your active order grants; get_shared_order reads one permitted issue and its shared design/file manifests and responses. These tools require read access and cannot invite, delegate or approve.
  • Customer portals: list_customer_portals, create_customer_portal, get_customer_portal, revoke_customer_portal, share_portal_document, unshare_portal_document.
  • Companies: create_company, set_company_member_role, remove_company_member, create_company_team, add_team_member, remove_team_member, delete_company_team, list_company_shares, share_company_revision, revoke_company_share.
  • Connectors and webhooks: list_company_connectors, create_company_connector, delete_company_connector, test_company_connector, get_webhook_deliveries.
  • Native applications: register_native_connection, list_native_connections, get_native_connection, prepare_native_handoff, record_native_readback.

Each tool's own description and input schema are in tools/list.

For files up to 20 MB, use reserve_document_upload, upload_document_chunk and complete_document_upload; read larger files with download_document_chunk. Transfers retain the existing file-type, revision and storage limits.

Native connection tools record document identity, coordinate transforms, element mappings, handoffs and read-back evidence with stale-head protection. A native application adapter performs and validates the actual edit.

Design hub

A design can be given an address. Anything that can send an HTTP POST, a mail forwarder, a chat workflow, a shell one-liner, posts a message to https://app.nordform.ai/hub/<token> as JSON (text, with from, subject and via if you have them) or as a form. The token is the whole credential: it is shown once when the address is opened and can be replaced or closed at any time.

What happens to a message: it is kept on the design as it arrived, read by the decision model as a change request, a decision, a question or information, and a change request is run as a proposal on the latest version, on its own if the owner chose that or when the owner presses run. The run's candidates come back as scenarios with their checks. Nothing a message says changes the design: a scenario becomes a version only when a person adopts it, through the same step a chat proposal needs. The design's stream, every message, reading, run and result in order, is read at /api/projects/{id}/hub/events.

Two more addresses come with it, shown at the same time:

  • …/hub/<token>/slack is the request URL for a Slack app subscribed to app_mention and message.channels. Every request is checked against the app's signing secret, which you paste into the Hub panel first; a person's message or mention in the channel becomes a message on the design, and Slack's own retries are stored once.
  • …/hub/<token>/mail takes a forwarded mail as a form, JSON or a raw message: a Gmail Apps Script, SendGrid Inbound Parse, a Mailgun route or Postmark inbound. The reply is kept without the quoted thread under it, and a forwarder that fires twice stores one message.

Responses on the design's own deliverables arrive the same way. While the hub is open, a comment or reply a reviewer leaves in one of the design's reviews, and an expert's change request, becomes a message with via: "review", the reviewer's name as the review shows it, and their text; notes brought back from another tool's BCF file become messages with via: "bcf". Each is tied to the version the reviewer was looking at and links back to the review, and the review shows its owner whether it reached the inbox. Reviewers are told, above the box they write in, that their comments go to the owner's design inbox and are read by our model; their review carries inboxForwarding and nothing else about the hub. The same item never arrives twice, the address's limits apply to it, and the owner can switch this off with POST /api/projects/{id}/hub/responses {"forward": false}. The owner's own replies stay in the review. A comment left on one of the blocks a project's delivery document draws from a saved version (its sheets, decision register or version history) arrives the same way, with via: "live document", under the same switch and with the same notice above the comment box; a comment on the text people write, and the owner's own comments, stay in the document.

To hear back, subscribe a webhook destination to hub.message.received, hub.scenarios.ready or hub.run.failed. What a person does in the workspace on a design with an address is on the same stream, and has four more events: hub.package.issued, hub.signoff.given, hub.decision.recorded and hub.version.adopted; GET /api/projects/{id}/actions lists the latest. Like every webhook, these carry identifiers only; read the message and its scenarios from the API as yourself. The MCP server offers read_design_hub, post_design_message and act_on_design_message.

Answers where a message came from. Once a message has an outcome (scenarios ready, kept without a run, or failed), the design can answer it once. In Slack, through the Nordform Slack app: one reply in the message's own thread saying how it was read, how many scenarios ran and their check counts, with a link. A channel message that ran nothing gets no reply; a mention does. By mail, only to addresses the design's owner approved in the Hub panel, because the mail address is public and a From line can be forged; the mail carries the link and why it was sent, nothing about the design. The link opens a read-only page with the message, its reading and the scenarios with their checks for 30 days, or until the owner closes or replaces the address. Only the owner can adopt a scenario, in the workspace. The owner can turn answers off.

Answers from the record. On a personal design, a message read as a question gets an answer drafted from the design's record: the latest version's inputs, the checks of a finished run on that version, the standing decisions, its sign-offs and the requirements in force, each fact with its source. No run starts and nothing is charged. The draft is cut to what the asker may see before it is written, and a quantity the message states is compared with the version at the precision stated. The draft is listed in GET /api/hub/waiting and sent or dismissed, once, with POST /api/projects/{id}/hub/answers/{messageId}. Send answers in the message's Slack thread, or mails an approved sender a link to a page showing what was sent; nothing goes out before it. A company design answers in its own Slack channel.

Kept scenarios. A scenario from a run can be kept with POST /api/projects/{id}/scenarios and {jobId, candidateId}. It is copied from the run's own result, pinned to the version the run answered, and outlives that result. GET on the same route says, for each kept scenario, whether it is out of date because the design's inputs, the kernel it was computed with, or the version or rule pack of one of its checks has moved on. Company designs have the same routes under /api/company-designs/{companyId}/{designId}/scenarios.

Connect Slack. The Hub panel can install the Nordform Slack app into your workspace with one click: you choose the channel on Slack's own consent screen, the app joins it (a private channel needs an /invite), and messages and mentions there reach the design. The app asks for incoming-webhook (the channel picker), app_mentions:read, channels:history, channels:join and chat:write, and nothing that reads private channels, direct messages or files. One channel connects to one design. The per-design Slack link above keeps working for a Slack app you build yourself; it receives messages but does not answer them.

Mail reaches a design at its own hub address, or through a forwarder in front of your mailbox that posts it. A worked story on a cooling plant is in the demo. Connect Slack needs the Nordform Slack app configured on the deployment; until it is, the panel says so, and the Slack link above works with a Slack app of your own.

A company project can be given an address the same way, at /api/company-designs/{companyId}/{designId}/hub with the same actions and stream. A company administrator or an editor of the project opens, replaces and closes it; everyone with access to the project reads its messages and stream; only an editor with run authority runs a message, or lets runs start automatically, and those runs are started as that editor. A run on a company project is charged to the company allowance and counts against the project's daily run limit, never against anyone's personal credits, and its proposals are reviewed and saved from the project's run list like any company run. Company webhook destinations can subscribe to the three message events, hub.message.received, hub.scenarios.ready and hub.run.failed, identifiers only; the four workspace action events are personal only. The MCP tools read and act on personal projects' hubs only. A company project answers in its own connected Slack channel, and not by mail.

Issued packages and review codes

A prepared package of a version (its drawings, lists and schedules) is issued to people already invited to a review of the project: GET|POST /api/projects/{id}/package-issues with {documentId, inviteIds}. Each person is mailed a link to the review, never the file, and the record keeps when it was issued, when they first opened it, and which newer package replaced it.

The person a package was issued to returns it coded with POST /api/reviews/{reviewId}/documents/{documentId}/code and {code, comment}. The code is approved, approved-with-comments or rejected, and the last two need a comment. One code per issue, tied to the version issued, and never changed: a resubmission is a newer package issued again. A code changes nothing in the design; only adopting a version does. The owner is told in their notices, and on the design's hub when it has one.

GET /api/projects/{id}/interface-record answers "agreed when, by whom, on which version" for each design one of your designs reads as its host: who issued the host version it reads and when, who returned which code on it, which version of the reading design took it in, and how many host versions have been saved since. It is read from records that already exist and guesses nothing; a host version never issued says so. It covers links whose reading design is in your own account; for a host in another account or company it names the version read but does not show that side's issues or codes.

Hosts in another company. A company administrator shares a company design with another company as a host with POST /api/company-designs/{companyId}/{designId}/host-shares: from one version, optionally through a later one, at a level (Interface only, Model only, or the model with the version's brief and context). Members of the receiving company see it in GET /api/hosts and read one version with GET /api/hosts/{source}/{id}, cut to what the share allows; a version the share does not cover is a 404. A pipe run or final circuits that names such a host asks for a link, which the host's side confirms with POST /api/design-links/{dependentId}/confirm; either side withdraws it with …/withdraw. Only a confirmed link is recalculated when the host gets a newer version the reader may see, and recalculating charges neither side. A reader shared the interface only is told what changed at the interface and is not recalculated.

Webhooks

Instead of polling, have Nordform call your own server. Add a destination in the workspace → API & MCP access, or with POST /api/webhooks under the admin permission, giving a label, an https endpoint and the events you want. Up to ten per account.

Event When
job.succeeded, job.failed, job.cancelled A cloud job reached a terminal state
project.created A project was created
document.ready An upload completed
render.ready, render.failed A render finished
review.comment.created Someone commented on a review you own
review.decision.recorded An expert recorded a decision on a review you own
document.issued A package of one of your projects was issued to a person
revision.issued A version of one of your projects was issued for the first time
document.coded A person returned a package you issued to them, coded; the payload carries the code but not the comment
hub.message.received, hub.scenarios.ready, hub.run.failed A message reached a design's address, its run's scenarios are ready, or its run failed or was refused
hub.package.issued, hub.signoff.given, hub.decision.recorded, hub.version.adopted On a design with an address: a package was issued, a sign-off given, a decision recorded or a version adopted

A delivery is a POST carrying one event:

{"id":"…","event":"job.succeeded","createdAt":1757000000000,
 "data":{"jobId":"…","projectId":"…","revisionId":"…","status":"succeeded"}}

What travels is identifiers and status, never content. No project name, brief, comment text, signoff statement, document, or email address ever leaves in a webhook. Read the resource back from the API as your account to see it.

Verify the signature before acting on a delivery. Each destination has its own secret, shown once when you create it. The Buildable-Signature header is v1= followed by the hex HMAC-SHA256, keyed with that secret, of {Buildable-Timestamp}.{raw body}. Compare it in constant time and reject a timestamp far from your own clock; the timestamp is what stops a captured body being replayed. Nordform-Delivery carries a stable id, so a retry is safe to make idempotent.

Anything answered outside 2xx — including a redirect — is retried at five-minute intervals for up to eight attempts, then dropped after a week. GET /api/webhooks/{id}/deliveries shows what is queued, sent or dead, and the destination row records the last status. A destination is disabled rather than deleted, so its history stays readable.

Endpoints must be https on a public host, with no credentials in the URL. Nordform stores the URL but never shows it back in full: the list shows the host and the path length only.

Company webhooks

A company workspace on the enterprise plan can subscribe its own servers to company events (a project version saved, a check or generation finishing, a record or package saved, a package downloaded, a revision shared, membership or policy changes, part data on a Standards library component written). Company administrators add these in the company workspace, not through a personal token, and they are separate from the personal destinations above in every respect: their own list, their own signing secrets, their own queue.

Three things differ from personal destinations. A new company destination is challenged first: the first POST carries the event webhook.verify and a challenge value, and nothing else is sent until your server answers with a 2xx status and a body containing that value. The secret can be rotated, and for one day after a rotation the Buildable-Signature header carries two comma-separated v1= values so you can roll over without a gap. Failed deliveries back off from a minute to a day over eight attempts, then wait in a dead-letter list from which an administrator can replay them; every delivery also carries a Nordform-Idempotency-Key header, which is stable across attempts and replays.

The same rule about content applies: identifiers and status only. Read the resource back through the API as a member of the company.

Errors

A refusal from Nordform always arrives as JSON with an explanation.

Status Meaning
401 The token is missing, malformed, expired or revoked
402 Not enough credits remain on the account
403 The token lacks the permission this request needs, or the route is browser-session-only
404 Not found, or not yours
409 An idempotency key was reused for different inputs, or a project changed elsewhere
429 The token's request limit for the last minute is spent (with Retry-After), its run budget is used up, or an account job cap was reached

Over MCP, a refusal comes back as a tool result marked as an error carrying the same status and message, so an assistant can read it and react. Malformed arguments and unknown tool names are JSON-RPC errors instead.

A 403 with error code: 1010 in the body did not reach Nordform. Our edge protection turned it away first, usually because the client sent no User-Agent or a default one such as Python-urllib/3. Set a User-Agent naming your tool and the request goes through. Command-line curl, Node's fetch and the common MCP clients are unaffected.

Keeping tokens safe

Treat a token like a password: it acts as you. Give each tool its own token, so one can be revoked without disturbing the others. Prefer a short expiry for a laptop and a small run budget for anything experimental. Revoke a token the moment a machine is lost or a contract ends; revocation takes effect immediately.

Nordform stores only a hash of the secret, never the secret itself. A token's permissions, expiry and budget are fixed when it is created.

Your customer's connection

The company that ordered a design does not need an account, a token of yours or any of the above. The page they open from the link you sent them is their order view: the design and its drawings, the status of the offer, every revision you have sent them and what changed between those revisions, each file with its SHA-256, what evidence stands behind the revision on the page and what does not, their answer, and their change requests in their own words. From that page they can download everything they were sent as one JSON file.

From the same page they can make a connection token, so that their own assistant or system reads the order over MCP. The token is minted by the person holding the link, carries exactly that link's trust and ends when the link ends or when you withdraw the offer. A client that signs in through the browser instead, such as a connector in the Claude app, points at the same https://app.nordform.ai/mcp; the authorization page then asks who is connecting, and your customer chooses I received a design and an offer, pastes their link, and approves. What they get is the same connection token, ending when the link ends, and they can revoke it on their page. It reaches https://app.nordform.ai/mcp and nothing else, and it offers ten tools: read the order, the revisions sent, what changed, the deliverables, the evidence, the change requests; download a file; export the order; ask for a change; answer the offer. It cannot change the design, start work, spend anything or reach any other order. A change request they make appears in your workspace under the offer, where you answer it by naming the revision that carries the change, or decline it with a reason. Your run budget and your credits are never touched by a customer's connection.

Scope

Tokens belong to a person and always act as their owner. The MCP server offers tools and two read-only resources, nordform://queue (the decisions and assignments waiting on the token's owner) and nordform://project/{projectId}/hub (a design's hub stream); an assistant reads them when it asks. An accepted or rejected opening is recorded on its comment and announced by the usual comment event, and the check result stays as it was. Library part data is written for up to 200 components per batch and never creates components: a component enters the library only when a named engineer accepts it in the workspace. A customer's connection reads the order, asks for changes and answers the offer, and the customer hears of a new offer by email.

All connectors · Nordform for practices · Open workspace

Versioned interface readings. GET /api/hosts/{source}/{id}?form=interface&projection=2&revision={revisionId} adds engine-hall and boiler-house equipment interfaces to the projection. Omit projection for the original projection. Use projection=3 to include process-plant schematic connection points and their media/nominal sizes as well. These are concept positions; process support loads, clearances and internal routes come from the equipment maker. Saved family interface references record projectionVersion: 2 or 3 for the selected form; absent means the original form. Reopening and comparison must request the recorded projection to preserve the saved hash.

Company industrial concepts. Company-design creation and version saves accept engine-hall, boiler-house, process-plant, pipe-run, electrical-circuits, steel-portal, skid-frame, access-platform, fabric-hall, roof-truss and foundation family projects. Company steel portals, skid frames, access platforms, fabric halls, roof trusses and foundations can be read as structural sources through the existing model-host routes with current source access and immutable version/hash pins. Saving requires editor access, the current company version and exactly one appended immutable revision. Editors can upload company-owned originals with POST /api/company-designs/{companyId}/{designId}/source-files?revisionId={id}&version={version}&name={fileName} (raw bytes, up to 20 MB), and authorized design readers download with GET .../source-files/{fileId}. Saved plant readings and KKS/DEXPI import records must reference this design’s ready originals with matching hashes. Process inputs may retain an adoptedPid snapshot with the exact saved DEXPI source record and bounded mapped equipment, nozzle-to-nozzle lines and valves. Explicit adoption appends a revision; the layout uses standard envelopes and positions. Drawing-set plant/verify requests from company designs carry X-Company-Id and X-Company-Design-Id; they require editor access and refuse confidential designs before calling a provider. Company family snapshots may name one saved interface reading in interfaceFollowing. The derived link belongs to the company design. GET .../following returns confirmation and accessible interface changes; /api/company-interface-links/{designId}/{confirm|withdraw|request} manages source confirmation. Changed-interface notices require a confirmed link and current source access; they do not adopt a revision or start a run. Confidential dependent designs cannot request following. Family cloud runs and personal-document imports are not supported on this path.

Register-item responses. An issued personal interface register includes all its frozen rows. Its recipient may post {itemKey, response: {code, comment}} to /api/reviews/{reviewId}/documents/{documentId}/item-code. The codes are approved, approved-with-comments and rejected; the latter two require a comment. A response is immutable and belongs to that recipient’s issue and row, separate from the package code. Current review and file access are required.

Company interface registers. GET /api/company-designs/{companyId}/{designId}/interface-registers lists saved registers and visible issues. Editors post {action:"prepare", revisionId, version} or {action:"issue", recordId, recipients:[userId]}; issuing uses existing design access (up to 20 recipients per request and 50 per register). An issued recipient posts {action:"respond", issueId, itemKey?, response:{code,comment}}; omit itemKey for the overall code. Codes are immutable and require current design access. GET .../interface-registers/{recordId} downloads checksum-verified ZIP bytes. Registers cover company engine halls, boiler houses, process plants, pipe runs and electrical circuits, stay company-owned, and grant no engineering approval.

Personal interface-register preparation also supports process plants, pipe runs and electrical circuits. Pipe and electrical penetration requests use the saved assumed zone or the pinned structural host with current model access and a matching input hash. Deriving a new register refuses a missing or changed host, never replacing it with an assumed zone. An existing frozen register remains retrievable. Process connections are schematic. Process registers can also derive penetration requests from routed lines against a pinned structural host, with the same access and hash checks. No building boundary is inferred. Company process, pipe and electrical saves validate new host references, and company register preparation rechecks the host before deriving a new register; existing frozen registers remain retrievable. Company process, pipe and electrical designs can optionally set structuralFollowing on a saved structural host. Confirmed links detect member changes using current model access to both versions. GET .../following returns bounded member-change counts and the compared source revision. Editors explicitly adopt that version; notifications never change the saved pin or start a cloud run.

Process inputs may include buildingBoundary with min and max XYZ triples in metres, in the process frame. Each maximum must exceed its minimum; coordinates are limited to 10,000 m from the origin. Omitting it leaves boundary crossings unstated. The register derives entry and exit points from the saved routed lines and this explicit inside box. Boundary inputs are immutable with the revision.

Saved industrial alternatives. Engine-hall, boiler-house and process-plant snapshots may contain an append-only hallScenarios array of up to 100 records, each with id, name, revisionId, savedAt and optional rateId. References must name a revision and saved tender rate table within the same snapshot. Existing records cannot be changed or removed. Personal saves use the existing snapshot concurrency guard; company saves require current editor access, the current company version and one appended immutable revision. Comparisons select two to four alternatives and recalculate from their pinned inputs with the current kernel. The existing 100-revision limit also applies to company saves. Process comparisons include equipment envelopes, modelled piping and process checks; imported drawing overlays, structural-host checks and building quantities are excluded. This is separate from run-generated kept scenarios.