SDK & REST API
Drop Wellbore Genius solvers into your notebook or pipeline. JSON over HTTPS, bearer-token auth, team-scoped. Mint a key under Settings → SDK API keys.
Every request must include an Authorization header with a bearer token minted from Settings. Tokens look like dh_live_… and are shown only once at mint time. Revoke under Settings; last_used_at bumps on every successful call.
Returns the full solver-spec scoreboard — every coupling kernel's "Today" vs "Roadmap" stance. Stable JSON shape, mirrors the in-app /solver-spec page.
curl https://wellboregenius.com/api/public/sdk/v1/solver-spec \
-H "Authorization: Bearer dh_live_..."Drives the non-planar 3D fracture pipeline (DDM + tip kinking + optional out-of-plane tilt and stress-driven curvature) on a synthetic rectangular mesh. Returns per-step history + final mesh summary.
curl -X POST https://wellboregenius.com/api/public/sdk/v1/non-planar-3d/run \
-H "Authorization: Bearer dh_live_..." \
-H "Content-Type: application/json" \
-d '{
"halfLengthFt": 300,
"halfHeightFt": 150,
"elementSizeFt": 30,
"ePrimePsi": 4500000,
"defaultNetPressurePsi": 350,
"defaultSigmaHPsi": 4200,
"advanceFt": 25,
"maxKinkDeg": 10,
"steps": 8
}'A pip install downhole wrapper will ship separately. In the meantime, a dependency-free typed client (stdlib only) is bundled at /sdk/python/downhole_sdk.py. It includes typed dataclasses + Bearer auth for parent_child_analyze(). Or call the API directly:
# pip install requests
import requests
API_KEY = "dh_live_..."
BASE = "https://wellboregenius.com/api/public/sdk/v1"
H = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def solver_spec():
r = requests.get(f"{BASE}/solver-spec", headers=H)
r.raise_for_status()
return r.json()
def non_planar_3d_run(**kwargs):
r = requests.post(f"{BASE}/non-planar-3d/run", headers=H, json=kwargs)
r.raise_for_status()
return r.json()
def parent_child_analyze(parents, child, reservoir, **kwargs):
payload = {"parents": parents, "child": child, "reservoir": reservoir, **kwargs}
r = requests.post(f"{BASE}/parent-child/analyze", headers=H, json=payload)
r.raise_for_status()
return r.json()
# Simulations CRUD ----------------------------------------------------------
def list_simulations(workspace_id=None, limit=200):
params = {"limit": limit}
if workspace_id:
params["workspaceId"] = workspace_id
r = requests.get(f"{BASE}/simulations", headers=H, params=params)
r.raise_for_status()
return r.json()["simulations"]
def create_simulation(workspace_id, name, **kwargs):
payload = {"workspaceId": workspace_id, "name": name, **kwargs}
r = requests.post(f"{BASE}/simulations", headers=H, json=payload)
r.raise_for_status()
return r.json()
def get_simulation(sim_id):
r = requests.get(f"{BASE}/simulations/{sim_id}", headers=H)
r.raise_for_status()
return r.json()
def update_simulation(sim_id, **patch):
r = requests.patch(f"{BASE}/simulations/{sim_id}", headers=H, json=patch)
r.raise_for_status()
return r.json()
def delete_simulation(sim_id):
r = requests.delete(f"{BASE}/simulations/{sim_id}", headers=H)
r.raise_for_status()
return r.json()
if __name__ == "__main__":
print(solver_spec()["count"], "scoreboard rows")
np3d = non_planar_3d_run(
halfLengthFt=300, halfHeightFt=150, elementSizeFt=30,
ePrimePsi=4_500_000, defaultNetPressurePsi=350,
defaultSigmaHPsi=4200, advanceFt=25, maxKinkDeg=10, steps=8,
)
print("np3D steps:", len(np3d["history"]), "final mesh:", np3d["finalMesh"])
pc = parent_child_analyze(
parents=[
{"id":"P1","heel":{"x":0,"y":660},"toe":{"x":10000,"y":660},"drawdownPsi":1000},
{"id":"P2","heel":{"x":0,"y":-660},"toe":{"x":10000,"y":-660},"drawdownPsi":1000},
],
child={"id":"C1","heel":{"x":0,"y":0},"toe":{"x":10000,"y":0},"stageCount":50},
reservoir={"biotAlpha":0.85,"poissonRatio":0.22},
)
print("parent-child worst stage:", pc["summary"]["worstStageId"])
sim = create_simulation("default", "bakken_baseline", tags=["history-match"])
update_simulation(sim["simulation"]["id"], status="completed",
runtimeSeconds=1843, results={"peakBhpPsi": 9120})
print("simulations on team:", len(list_simulations()))Runs the analytical parent–child interference engine. Returns per-stage Δp, Δσ_h, asymmetry %, nearest parent, and bashing-risk chip — plus the rolled-up summary.
curl -X POST https://wellboregenius.com/api/public/sdk/v1/parent-child/analyze \
-H "Authorization: Bearer dh_live_..." \
-H "Content-Type: application/json" \
-d '{
"parents": [
{"id":"P1","heel":{"x":0,"y":660},"toe":{"x":10000,"y":660},"drawdownPsi":1000},
{"id":"P2","heel":{"x":0,"y":-660},"toe":{"x":10000,"y":-660},"drawdownPsi":1000}
],
"child": {"id":"C1","heel":{"x":0,"y":0},"toe":{"x":10000,"y":0},"stageCount":50},
"reservoir": {"biotAlpha":0.85,"poissonRatio":0.22}
}'Response schema (200 OK)
stages[] — one entry per child stage, ordered heel→toe:
stageIndex(number) — 0-based index.stageLabel(string) — 1-based UI label, e.g."Stage 12".midpoint({x, y}) — stage centroid in plan view [ft].depletionPsi(number) — depletion-induced Δp at the stage [psi].dSigmaHPsi(number) — Eaton Δσ_h from depletion [psi].asymmetryPct(number, −100..+100) — signed half-length skew toward the more-depleted side.nearestParentId(string | null) — id of closest parent, or null if none.nearestParentFt(number) — perpendicular distance to nearest parent [ft].bashingRisk("low" | "watch" | "high") — frac-hit severity chip.
summary — roll-up across all stages:
highCount,watchCount,lowCount(number) — stages in each risk bucket.worstStageId(string | null) —stageLabelof the highest-Δp stage.worstDepletionPsi(number) — max Δp across stages [psi].meanDepletionPsi(number) — mean Δp across stages [psi].meanDSigmaHPsi(number) — mean Δσ_h across stages [psi].
Example response
{
"stages": [
{
"stageIndex": 0,
"stageLabel": "Stage 1",
"midpoint": { "x": 100, "y": 0 },
"depletionPsi": 312.4,
"dSigmaHPsi": 187.6,
"asymmetryPct": 4.2,
"nearestParentId": "P1",
"nearestParentFt": 660,
"bashingRisk": "watch"
},
{
"stageIndex": 1,
"stageLabel": "Stage 2",
"midpoint": { "x": 300, "y": 0 },
"depletionPsi": 540.1,
"dSigmaHPsi": 324.0,
"asymmetryPct": -8.7,
"nearestParentId": "P2",
"nearestParentFt": 660,
"bashingRisk": "high"
}
],
"summary": {
"highCount": 1,
"watchCount": 1,
"lowCount": 0,
"worstStageId": "Stage 2",
"worstDepletionPsi": 540.1,
"meanDepletionPsi": 426.25,
"meanDSigmaHPsi": 255.8
}
}Team-scoped simulation rows. Same shape used by the in-app Cloud simulations page. Status must be one of not_yet_submitted, queued, running, completed, failed, cancelled.
# List
curl https://wellboregenius.com/api/public/sdk/v1/simulations \
-H "Authorization: Bearer dh_live_..."
# Create
curl -X POST https://wellboregenius.com/api/public/sdk/v1/simulations \
-H "Authorization: Bearer dh_live_..." \
-H "Content-Type: application/json" \
-d '{"workspaceId":"default","name":"bakken_baseline","tags":["history-match"]}'
# Retrieve (includes results JSON)
curl https://wellboregenius.com/api/public/sdk/v1/simulations/<id> \
-H "Authorization: Bearer dh_live_..."
# Update status / attach results
curl -X PATCH https://wellboregenius.com/api/public/sdk/v1/simulations/<id> \
-H "Authorization: Bearer dh_live_..." \
-H "Content-Type: application/json" \
-d '{"status":"completed","runtimeSeconds":1843,"results":{"peakBhpPsi":9120}}'
# Delete
curl -X DELETE https://wellboregenius.com/api/public/sdk/v1/simulations/<id> \
-H "Authorization: Bearer dh_live_..."- POST
/sensitivity/run— OFAT factor sweeps - Published
pip install downholewith typed dataclasses