INTEGRATIONS
REST API
Call Converge engines and read or write projects over HTTPS with an API key. Base URL, endpoints, errors, rate limits and the OpenAPI spec.
Overview
The REST API runs every Converge engine function on the server and lets your own programs list, read and save projects. It is versioned under /v1, takes JSON in and returns JSON. You authenticate with a personal API key, which acts as the member who created it: same company, same permissions, same licence seats.
The engine functions are the same ones you call from scripts, with the same inputs and outputs.
Base URL
The API is served from the same address as the app:
https://<your-app-host>/v1Integrations > REST API in the app shows the exact base URL for your deployment, and every snippet there uses it.
OpenAPI spec
An OpenAPI 3.1 description is public (no key needed):
https://<your-app-host>/v1/openapi.jsonIt is generated from the same engine catalogue and key scopes the server runs on, so it lists every engine function with its JSON Schema input and an example. The OpenAPI 3.1 button on Integrations > REST API opens it.
Authentication
- Create a key on Integrations > API keys.
- Keep it in an environment variable,
CONVERGE_API_KEY. - Send it on every request:
Authorization: Bearer cvg_...A key is cvg_ followed by 43 characters. A request with a bearer header never falls back to a browser session cookie. Each endpoint needs a scope on the key; see the table below and API keys.
Endpoints
| Method and path | Scope | What it does |
|---|---|---|
GET /v1/me |
any valid key | The member, company, plan and licences behind the key. |
GET /v1/engine/functions |
any valid key | Every engine function: name, module, title, description, input schema, example. |
GET /v1/engine/functions/{name} |
any valid key | One function's details. |
POST /v1/engine/functions/{name} |
engine:run |
Runs the function. The body is its input. |
GET /v1/projects |
projects:read |
The company's saved projects. |
GET /v1/projects/{id} |
projects:read |
One project with its data. |
PUT /v1/projects/{id} |
projects:write |
Creates or replaces a project. |
DELETE /v1/projects/{id} |
projects:write |
Deletes a project. |
GET /v1/projects/{id}/versions |
projects:read |
The project's saved versions. |
POST /v1/projects/{id}/versions |
projects:write |
Saves a named version. |
POST /v1/mcp |
any valid key; each tool checks its own | The MCP server. |
Every other endpoint (team, seats, AI, key management, roles, sign-off, admin) refuses API keys with 403. You sign in to the app for those.
Run an engine function
POST /v1/engine/functions/econ.metrics
Authorization: Bearer cvg_...
Content-Type: application/json
{ "cashFlows": [-1000, 300, 400, 500], "discountRates": [0.1], "discounting": "start-period" }The answer has this shape:
{ "name": "econ.metrics", "module": "econ", "result": { "npv": [...], "irr": ..., "paybackYears": ..., ... }, "ms": <server time> }name and module identify the call, result is the function output, and ms is the server time in milliseconds. Running a function also needs the member's permission to run scripts. An empty body is read as {}. The body limit is 4 MB.
Units are field units by default: psia, degF, STB/d, Mscf/d, scf/STB, ft, in, md, cp, days. Add "units": "metric" at the top level of the body to send and receive bara, degC, m, sm3/d and sm3/sm3. Unknown input keys are refused with the list of valid keys.
The list from GET /v1/engine/functions also includes project fluid-model tools named eos.model.*: the AI Engineer's compositional tools, run read-only on a saved project's fluid models. They need projectId in the body and a key with both engine:run and projects:read.
Save a project
PUT /v1/projects/{id}
Content-Type: application/json
{ "name": "Field A", "data": { ... } }id: 1 to 64 letters, digits,_or-.name: 1 to 200 characters.data: the project data, at most 1.5 MB as JSON.
The answer is { "project": ... } with 201 when the project is new and 200 when it replaced an existing one. Creating needs the member's create-project permission and editing needs the edit permission. The company's plan limit on the number of projects applies (402). The demo project and approved reporting versions are never saved. Deleting needs the delete-project permission, and a project that holds an approved reporting version cannot be deleted (409).
POST /v1/projects/{id}/versions takes { "label": "..." } (1 to 200 characters) and returns { "version": ... } with 201.
Seats
API access is the licensable Integrations module. Every key request holds the member's Integrations seat, and an engine call also holds the seat of the function's module (for example Well performance for well.nodal), exactly as if the member had opened the module in the app. The member and their scripts share one seat per module. Black-oil PVT needs no seat.
There is no heartbeat: each call refreshes the seat, and it is released 3 minutes after the last call. If every seat is taken, the answer is 409 no_seat with details.holders naming who holds them and since when.
Rate limits
| Limit | Value |
|---|---|
| Requests per key | 120 per minute, REST and MCP together |
| Failed key attempts | 20 per minute per client address |
| Engine busy | 429 with Retry-After: 30 |
A 429 answer carries a Retry-After header in seconds, and retryAfter in the body. Engine calls run on the server with a time limit; a calculation that runs past it returns 422 engine_error with details.timeout: true.
Errors
Every error has the same shape:
{ "error": { "code": "validation_failed", "message": "...", "details": { "fields": { "fluid.api": "..." } } } }| HTTP | Code | When |
|---|---|---|
| 400 | validation_failed |
Invalid input or JSON. details.fields names each field. |
| 401 | unauthenticated |
Missing, malformed, unknown, revoked or expired key. |
| 402 | plan_limit |
No licence for the module, no API access on the plan, or the project limit. |
| 403 | forbidden |
The key lacks the scope, the member lacks the permission, the endpoint refuses keys, or the account is disabled. |
| 403 | email_not_verified |
The member has not verified their email. |
| 403 | tenant_suspended |
The company account is suspended. |
| 404 | not_found |
Unknown endpoint, function or project. |
| 409 | no_seat |
Every seat of the module is in use. |
| 409 | conflict |
For example, deleting a project with approved reporting versions. |
| 422 | engine_error |
The calculation ran but could not produce a result for this input (out of range, no solution, time limit). |
| 429 | rate_limited |
Too many requests. |
| 500 | internal |
An error on the server. |
Snippets
Integrations > REST API > Snippets generates a ready-to-run call for any function in curl, Python or JavaScript, using that function's documented example input. The key is always read from CONVERGE_API_KEY, never written into the snippet.
curl -sS -X POST "https://<your-app-host>/v1/engine/functions/pvt.gasProperties" \
-H "Authorization: Bearer $CONVERGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"psia":3000,"TF":200,"gasSG":0.7}'import os
import requests # pip install requests
resp = requests.get(
"https://<your-app-host>/v1/projects",
headers={"Authorization": f"Bearer {os.environ['CONVERGE_API_KEY']}"},
timeout=30,
)
resp.raise_for_status()
for p in resp.json()["projects"]:
print(p["id"], p["name"], p["updatedAt"])