API reference

This is the JSON REST API for the complete agent simulation loop: author or import a model, solve and check it, then inspect, modify or re-submit the returned model. The base URL is https://www.physicsbase.dev. The same contract supports one-time simulation, verification and optimization workflows.

Introduction

Send a Problem object. You get a Result object back. The endpoint or the analysis field selects static, modal, buckling, or transient. There is no state between calls. Each solve is independent. Thus agents can send many solves at the same time.

bash · a complete requestcopy
curl -X POST https://www.physicsbase.dev/v1/solve \
  -H "authorization: Bearer $PB_KEY" \
  -H "content-type: application/json" \
  -d '{
    "nodes": [[0,0],[2,0]],
    "materials": {"steel": {"E": 200e9, "nu": 0.3}},
    "elements": [{"type":"truss2d","nodes":[0,1],"material":"steel","section":{"A":0.01}}],
    "supports": [{"node":0,"dofs":["ux","uy"]},{"node":1,"dofs":["uy"]}],
    "loads": [{"node":1,"fx":50000}]
  }'

Authentication

Each solve needs an API key. Send it as a bearer token. Make a free key at sign-up. It is unlimited in the research preview and needs no card. Send the key on each request:

http · authorization headercopy
Authorization: Bearer pk_live_…

A missing key or an unknown key returns 401. Manage your keys and use data on your dashboard. To try the API without a key, use the keyless POST /demo/solve endpoint. It runs small problems, has a rate limit, and needs no sign-up. It also runs the live demo on the home page.

Errors and status codes

Errors return a JSON body with a detail string. You can act on the string directly. Examples are a bad support, a singular model, and an unknown element.

CodeMeaning
200OK. The results are in the body.
401The API key is missing or not valid — {code: NO_API_KEY | INVALID_API_KEY}.
422The problem is not valid. The body gives {code, message, hint}. It tells you what to correct.
429This occurs only on the keyless /demo/solve endpoint. It has a limit of 30 requests each hour.
500An unexpected solve error. You can send the request again.
json · 422 bodycopy
{ "detail": {
    "code": "NO_SUPPORTS",
    "message": "model has no supports; stiffness is singular",
    "hint": "Add at least one support to restrain rigid-body motion." } }

Usage

In the research preview there is no solve quota. Each key is unlimited and free. The system still counts use for each key. This lets us learn how agents use the API. See your total at any time at GET /auth/usage. The keyless /demo/solve endpoint has its own limit of 30 requests each hour for each IP (429). See more on the pricing page.

API keys have explicit solve and account scopes. Create and list them with POST/GET /auth/keys, edit or revoke with PATCH/DELETE /auth/keys/{id}, and rotate with POST /auth/keys/{id}/rotate. Use solve-only credentials in agents and CI. Full secrets are returned only when created or rotated.

The Problem object

This is the one input for all endpoints.

FieldTypeDescription
nodes requirednumber[][]Coordinates, [x,y] (2D) or [x,y,z] (3D).
materials requiredobjectName → {E, nu, rho?, alpha?}. Orthotropic lamina: add {E1, E2, nu12, G12} and set the fibre angle in the element section.
elements requiredobject[]{type, nodes, material, section, id?}. See element reference.
supportsobject[]{node, dofs[], values?}. values = prescribed displacement.
loadsobject[]{node, fx?, fy?, fz?, mx?, my?, mz?}.
element_loadsobject[]{element, w[]} distributed load on a beam/frame.
springsobject[]{node, dof, k} grounded elastic support.
thermalobject[]{element, dT} temperature rise (needs material alpha).
body_forcesobject[]{element, b[]} force per unit volume.
tractionsobject[]{nodes[], traction[], thickness} edge/surface pressure.
gravitynumber[]Acceleration vector for self-weight, e.g. [0,-9.81].
analysisstringstatic (default), modal, buckling, transient, explicit_dynamics, and the advanced modes in the coverage matrix.
num_modesintModes returned for modal/buckling (default 6).
dt, n_stepsnumber, intTime step and step count for implicit or explicit transient. Explicit rejects dt above its computed critical value; use params.auto_dt and params.dt_safety to select it automatically.
estimate_time_error, target_time_errorbool, numberRun a Richardson dt/dt/2 estimate for supported transients (default true) and set its relative target (default 0.02).
record_iterationsboolAttach genuine nodal iteration/time-step states for supported solvers. Use with return_model for playback. Direct solves are labelled initial/direct; they do not fabricate iterations.
return_modelboolAttach the self-describing mesh, fields, viewer metadata, animation timeline and self-check under model.
dampingnumber[]Rayleigh [alpha, beta] for transient.
titlestringOptional label echoed in the summary.

The Result object

Static and transient use the same displacement and reaction shape. Modal and buckling return spectra. Each response has a summary.

json · static resultcopy
{
  "displacements": [ { "ux": 0.0, "uy": 0.0 }, … ],
  "reactions":     [ { "fx": -50000.0, "fy": 0.0 }, … ],
  "elements": [ { "type": "truss2d", "axial_force": 50000.0,
                "axial_stress": 5.0e6 }, … ],
  "summary": { "n_dof": 4, "max_abs_displacement": 5e-5,
                "analysis": "static", "solve_ms": 1.2 } }

POST /v1/solve

This endpoint runs the analysis in the analysis field of the problem. The default is static. This is the main endpoint. The other endpoints are shortcuts. /solve (no version prefix) is an alias.

Returns

A Result with displacements, reactions, forces and stresses for each element, and a summary. The element results depend on the type. Trusses give axial_force. Beams give moment_i/j. Continuum elements and solids give a stress vector and von_mises.

Identical normalized problems are cached. The cache is shared by REST, MCP, async jobs, scalar fields, and the in-process SDK. It is TTL-limited, LRU-bounded, and deep-copy isolated; concurrent identical misses run one solve. Numerical failures are never cached. Inspect summary.cache.hit and summary.cache.coalesced; aggregate counters are available at GET /stats.

POST /v1/jobs GET /v1/jobs/{id}

Async solves for long-running problems (202 + poll). POST /v1/jobs takes the same Problem body as /v1/solve and returns 202 at once: {"job_id", "status": "queued", "poll": "/v1/jobs/{id}"}. Poll the poll URL with the same key until status is done — the full Result is in the poll body; failed carries the usual error object. Polling costs nothing against the rate limit. DELETE /v1/jobs/{id} cancels a queued job. Job ids expire about an hour after completion.

POST /v1/solve itself auto-diverts problems over ~5,000 nodes/elements to a job (you get the 202 body). Force the behaviour with ?mode=async or ?mode=sync.

json · poll response (done)copy
{
  "job_id": "job_kX3…", "status": "done", "elapsed_s": 42.7,
  "meta": { "analysis": "static", "n_nodes": 84211, "mode": "auto" },
  "result": { … the same Result a synchronous solve returns … } }

POST /v1/solve · analysis: viscoelastic | creep

new Time-dependent materials (trusses). viscoelastic is a Prony-series generalized Maxwell solid: params.prony = [{"E", "tau"}, …], the material E is the instantaneous modulus and E_inf = E − ΣE_i. The internal variables use the exact exponential integrator, so stress relaxation under a held displacement follows σ(t) = ε₀(E_inf + ΣE_i e^{−t/τᵢ}) to the step tolerance. creep is Norton power-law creep dε_c/dt = A·σⁿ (params.A, params.n), integrated implicitly — for a constant-stress bar it reproduces ε = σ/E + A·σⁿ·t exactly. Both use dt, n_steps, and monitor, and return per-step histories plus per-element stress, creep strain, and branch states.

POST /v1/solve · composite layup sections

new Laminated sections. Give plate4, mitc4, shell4, shell_mitc4, or quad4 a section: {"layup": [{E1, E2, nu12, G12, thickness, angle}, …]}. CLT drives the element: A supplies membrane stiffness, D supplies bending, and six-DOF shell elements assemble the full B membrane–bending coupling for unsymmetric stacks. Shell results include ε0, κ, N, M, top/bottom stresses in laminate and fibre axes for every ply, peak ply von Mises, and optional Hashin indices when strengths are supplied. Plate-only and membrane-only elements use D or A under their explicit zero-mid-plane-strain or zero-curvature kinematics. Verified against Navier bending, effective laminate moduli, and exact constant-field ABD energy patches for both shell formulations.

POST /laminate

Solves the full classical-lamination-theory ABD response for a ply stack under in-plane resultants N and moments M. It returns mid-plane strains, curvatures and the same top/bottom ply-stress recovery used by the coupled shell elements.

POST /v1/solve · analysis: nltransient

new Nonlinear transient dynamics — implicit Newmark (average acceleration) with full Newton on the co-rotational tangent. Handles what linear transient cannot: dynamic snap-through, large rotations, cable-like stiffening. Co-rotational trusses and finite-strain quad4 continuum are supported. Same fields as transient (dt, n_steps, optional Rayleigh damping, monitor). Undamped runs return summary.energy_check — the mechanical-energy drift relative to peak energy; a large drift means dt is too coarse for the response, so the result carries its own quality signal.

POST /v1/solve · analysis: explicit_dynamics

new Native central-difference dynamics with row-sum lumped mass. The default path is small-strain linear for translational structural elements. Unmixed truss models may set params.nonlinear_geometry: true for co-rotational large-displacement kinematics; unmixed 3D tet4, or hex8 models use total-Lagrangian finite-strain Hencky elasticity. params.plasticity: true activates committed axial plasticity for trusses or logarithmic-strain J2 Gauss-point plasticity for solids, with top-level yield_stress and hardening. The solver computes the spectral critical step and rejects an unstable dt; set params.auto_dt: true and optionally dt_safety to choose it automatically. params.initial_velocity and initial_displacement accept one vector for every node, a full nodal array, or {"all": [...], "nodes": {"12": [...]}}. load_curve is [[time, scale], ...]. A frictionless kinematic wall is {axis, coordinate, side, restitution, nodes?}.

json · elastic impactcopy
{
  "analysis": "explicit_dynamics",
  "n_steps": 800, "dt": 0,
  "params": {
    "auto_dt": true, "dt_safety": 0.5,
    "initial_velocity": [-5, 0, 0],
    "wall": {"axis":"x", "coordinate":0,
             "side":"lower", "restitution":0.2}
  },
  "return_model": true
}
Scope: this mode covers linear elastic waves on translational elements and large-displacement elastoplastic dynamics on trusses and first-order 3D solids, including bounded rigid-wall impact. It is not nonlinear shell crash, material failure, shell folding, deformable/self-contact, or a vehicle-crash claim; OpenRadioss remains the named crash backend.

POST /v1/solve · analysis: contact

Unilateral frictionless contact by an exact active set — no penalty parameter. Two constraint types mix freely in the contacts list: a rigid barrier {node, dof, limit, side: 'upper'|'lower'} stops one DOF at a fixed value, and a deformable-to-deformable pair {node_a, node_b, dof, gap, sign?}new lets two bodies meet: it enforces sign·(u_a − u_b) ≤ gap, both bodies deform, and the transmitted force is solved for and reported per pair (compression positive). Pairs with gap ≤ 0 start in contact. For surfaces whose meshes do not line up, use node-to-surface contact {node, segment: [t1, t2]} in 2D or {node, facet: [t1, t2, t3]} in 3Dnew: the contactor node is projected onto the target and the one contact force is distributed to the target nodes by the projection shape functions (reported under node_forces). The summary lists each contact with in_contact and contact_force.

Opt-in spatial-convergence evidence. Set estimate_contact_error: true to run the declared contacts on the submitted mesh and on one uniformly h-refined supported mesh. The refined solution is returned. summary.contact_convergence compares total and per-contact force, maximum penetration/gap violation, and active-set stability. Give a contact a stable id and positive contact_area to include pressure and pressure convergence. Change the default 5% force/pressure threshold with target_contact_error.

json · contact convergence requestcopy
{
  "analysis": "contact",
  "estimate_contact_error": true,
  "target_contact_error": 0.05,
  "contacts": [{ "id": "interface-A", "node_a": 41,
                 "node_b": 86, "dof": "uy", "gap": 0.0,
                 "contact_area": 2.5e-4 }]
}
Scope: this is a one-level two-grid sensitivity check over truss/beam/frame, CST and quad4 refinement. It does not claim an asymptotic convergence rate, large-sliding accuracy, mortar contact, or physical validation.

POST /modal

This endpoint gives natural frequencies and mode shapes. It solves (K − ω²M) φ = 0. The materials must include rho. The system ignores applied loads.

json · responsecopy
{
  "frequencies_hz": [ 9.1, 57.0, 159.6, … ],
  "angular_frequencies": [ … ],
  "mode_shapes": [ [ {"ux":0,"uy":0,"rz":0}, … ], … ],
  "summary": { "num_modes": 6, "analysis": "modal" } }

POST /buckling

This endpoint does linear buckling. The applied loads give the reference pattern. The load_factors multiply the pattern to reach instability. The critical_loads give the magnitudes.

json · responsecopy
{
  "load_factors": [ 1842.1, … ],
  "critical_loads": [ 1842100.0, … ],
  "mode_shapes": [ … ],
  "summary": { "reference_load": 1000.0, "analysis": "buckling" } }

POST /transient

This endpoint gives the Newmark step response. The loads stay constant from t=0. The initial conditions are zero. Set dt and n_steps. The damping and monitor = [node, dof] fields are optional. By default the solver also runs dt/2 and reports a second-order Richardson estimate for the returned coarse response. Set estimate_time_error:false to opt out or change the 2% threshold with target_time_error.

json · responsecopy
{
  "times": [ … ], "peak_displacement": 0.0521, "peak_time": 0.018,
  "frames": [ {"t":…, "max_abs_displacement":…}, … ],
  "monitor": { "node": 1, "dof": "uy", "history": [ … ] },
  "summary": { "dt": 2e-4, "n_steps": 150, "analysis": "transient" } }

POST /bundle

This endpoint solves the problem and returns a complete, self-describing model. It is the same as any solve with return_model: true. The model is one JSON document. An agent can parse it, animate it, or send it back to the engine. It holds the mesh, the materials, the boundary conditions, the results, per-node colour fields, an animation block, and the full analysis-scoped self-check.

json · the model objectcopy
{ "model": {
  "schema": "physicsbase.model/1.0",
  "meta":  { "analysis": "static", "units": "SI", "n_nodes": 105, "n_elements": 84 },
  "mesh":  { "nodes": [ … ], "elements": [ { "type": "quad4", "nodes": [ … ] }, … ], "materials": { … } },
  "bcs":   { "supports": [ … ], "loads": [ … ] },
  "results": { "displacements": [ … ], "elements": [ … ] },
  "fields": { "von_mises": [ … ], "displacement_magnitude": [ … ] },
  "animation": { "kind": "deformation", "deformation": [ [dx,dy], … ],
                 "recommended_scale": 18.0, "color_field": "von_mises", "color_range": [ … ] },
  "self_check": { "balanced": true, "equilibrium_residual": 2.9e-10 } } }

To animate, place each node at mesh.nodes[i] + t · recommended_scale · animation.deformation[i] for t from 0 to 1, and colour it by fields[animation.color_field]. For modal and buckling, animation.modes holds one deformed shape and scale for each mode.

3D and recorded-state contract. The Copilot viewer always opens in an orbitable 3D camera; native 2D meshes are shown honestly as midsurfaces at z=0. With record_iterations: true, applicable animation.frames[] entries carry iteration, relative_residual, current nodal deformation and fields. The current implementation records real CG, geometric-Newton, Navier-Stokes Picard, contact active-set and sampled structural-transient states. Direct solves provide labelled initial/final states only. Modal and buckling playback is a sinusoidal phase animation, not solver convergence.

POST /v1/import

This endpoint reinterprets an external CAD or FE file as a native model, solves it, and returns the result on the geometry. You send the file in the request. CAD solids (.step, .stp, .iges, .brep) are meshed into tetrahedra. Mesh files carry their own elements: Abaqus .inp (also its materials, sets, and boundary conditions), Nastran .bdf/.nas, Gmsh .msh, CalculiX .frd, VTK/.vtu, STL, and more. Call GET /v1/capabilities to read the exact extension registry and whether each optional backend is installed in the running service.

json · requestcopy
{ "filename": "bracket.step",
  "content_base64": "<the file, base64-encoded>",
  "material": { "E": 2.1e11, "nu": 0.3 },
  "supports": [ { "axis": "z", "op": "min", "dofs": ["ux","uy","uz"] } ],
  "loads":    [ { "axis": "z", "op": "max", "fz": -5000 } ],
  "analysis": "static", "mesh_size": 0.5 }
Long-running imports. STEP/IGES/BREP and uploads over the configured threshold return 202 with {job_id, poll}. Poll GET /v1/jobs/{job_id} until status:"done"; the completed simulation is then under result. Use ?mode=sync or ?mode=async to force either path.

Boundary conditions

A raw CAD solid carries no loads or supports, so you name them with a small selector: by node id ({"node": 12}), by named set from the file ({"nset": "TOP"}), or by a coordinate plane ({"axis": "z", "op": "min|max|==", "value": .., "tol": ..}). A support adds "dofs"; a load adds force components (fx/fy/fz) which are the total, split over the selected nodes. An Abaqus .inp that already carries *BOUNDARY and *CLOAD needs none of this. INP material sections, generated sets, shell thickness, and direct/set BCs are retained. Unsupported safety-critical load/contact cards fail explicitly; they are never silently omitted.

Returns

The same result as /solve, plus the model bundle under model — the reinterpreted mesh, the results, and the self-check, all on the imported geometry. summary.import and model.meta.import include the source name and SHA-256, adapter/mesher, requested and resolved mesh size, coordinate scale, translated element counts, named sets, and linearisation warnings.

For security, remote clients should upload content_base64. Server-side paths are disabled unless an administrator configures PHYSICSBASE_IMPORT_ROOT, and are then confined beneath that root. Uploads are size-limited. Quadratic cells are intentionally linearised to corner nodes and orphan midside DOFs are compacted before solve; the provenance block records the transformation. Triangulated 3D surface-only STL/OBJ/PLY files need volume or quad-shell remeshing before solve. Abaqus assemblies/instances and full vendor-deck semantics remain outside this adapter version.

POST /demo/cad/{example_id}

Runs a curated, packaged CAD example without an API key. Unlike /v1/import, this route accepts no path and no file content: the server owns a hash-pinned STEP fixture and a reviewed material, mesh, support, load, and size envelope. Discover examples with GET /demo/cad.

bash · real STEP live solvecopy
curl -X POST https://www.physicsbase.dev/demo/cad/2020-corner-bracket \
  -H "content-type: application/json" \
  -d '{"load_n":100}'

curl -X POST https://www.physicsbase.dev/demo/cad/nasa-crm-wing \
  -H "content-type: application/json" \
  -d '{"load_n":5000}'

curl -X POST https://www.physicsbase.dev/demo/cad/uav-wing-gust \
  -H "content-type: application/json" \
  -d '{"load_n":4000}'

The current model is a CC BY 3.0 corner bracket for 20 mm aluminum extrusion, assigned 6061-T6 properties. One mounting face is fixed and 25–200 N of lateral racking is distributed over the perpendicular face. The response is the normal static result plus a complete model bundle. summary.cad_demo and model.meta.cad_demo record the upstream URL/commit, author, license, source SHA-256, material, boundary conditions, load, coordinate scale, and coarse-mesh policy.

nasa-crm-wing is the aerospace example: a 1–10 kN upward proof load on a scale-matched solid envelope derived from the measured bounds of NASA's official Common Research Model left-wing STEP. The metadata records both upstream and derivative hashes and explicitly states that the live asset is not the exact NASA outer mold line, internal wing structure, manufacturing drawing, or certification model. Source, assumptions, result, and limitations →

uav-wing-gust is the distributed-load example: a bounded 1–8 kN resultant half-wing load on an original swept 1.8 m STEP. The server extracts upper-surface boundary triangles and area-weights a pressure field so the integrated load per unit span follows an elliptical law. Add "direction":[x,y,z] to rotate that resultant in XY, XZ, YZ or full XYZ space; the server rejects zero/non-finite vectors and normalizes valid input. The returned metadata includes the generated geometry, load mapping, equivalent-solid material and refinement limits. Method, solved evidence, and downloadable STEP →

Scope. This endpoint is a transparent concept demo, not design certification. Always inspect the returned spatial-error estimate; use authenticated /v1/import with a refined mesh for release work.

GET /demo/automotive

Returns provenance, mesh counts, execution status, published reference evidence and an explicit claim boundary for precomputed automotive cases. The route is keyless and read-only; it never starts an explicit job.

bashcopy
curl -s https://www.physicsbase.dev/demo/automotive
curl -s https://www.physicsbase.dev/demo/automotive/2015-toyota-camry-ncap
curl -s https://www.physicsbase.dev/demo/automotive/openradioss-bumper-impact-probe

The current Camry entry records 2,273,631 nodes and 2,277,283 elements, the official archive SHA-384, OpenRadioss as the external numerical backend, the official four-core runtime estimate, and which work physicsbase has and has not completed. Published CCSA/LS-DYNA validation values are never returned as physicsbase-computed fields. The component entry records an interrupted trace through 14.9 ms, the 14.0 ms last complete field frame, solver hashes, and normal_termination: false.

GET /capabilities

This is a machine-readable list of all functions of the engine. It gives the element types, DOF names, load components, analyses, and more. An agent must call this endpoint first to build a valid problem.

bashcopy
curl -s https://www.physicsbase.dev/capabilities

GET /health

This endpoint shows that the service is available. It returns {"status":"ok","version":"…"}. It needs no key.

Do you prefer direct tools? The same functions are available over MCP (fem_solve, fem_modal, fem_buckling, fem_transient) and a Python SDK. See the docs.