PiQ SaaS API
PiQ SaaS is a web API at https://saas.parametriq.co.uk: requests over HTTPS, answered with JSON (or the file asked
for). It is described by an OpenAPI document, PiQ SaaS API, version 1, at
https://saas.parametriq.co.uk/openapi.json, from which clients can be generated in most languages: see
OpenAPI.
Every request must carry your API key in the X-API-KEY header: see Using your API Key.
Templates and values
Every operation works on a template: a ParametrIQ drawing's text (a .piq file), sent as the template. The
drawing it makes depends on its labelled variables, those defined with a label:
L=400,"Length (inside)"
W=300,"Width (inside)"
T={"E flute":1.5,"B flute":3}["B flute"],"Material"
HANDHOLES=FALSE,"Hand holes"
Send values for them by name (any case): a number, text, or true or false; for a choice, its name ("E flute")
or its place in the list (0). Variables left out keep the template's values. Only labelled variables can be given
values: a value for any other variable is refused, as is a value of the wrong kind. Choose the units with units:
Millimeters (the default) or Inches.
{
"template": "L=400,\"Length\"\nW=300,\"Width\"\nP1=[L,0]\nP2=[L,W]\nP3=[0,W]\nPOLYLINE @1,[P0,P1,P2,P3],TRUE,0",
"values": { "L": 500, "W": 250 }
}
Instead of sending the template, you can keep templates in your own S3-compatible storage and name one as the
source: "source": { "storage": "main", "key": "templates/box.piq" } (see Your storages below).
Each request runs the template afresh: nothing is kept between requests.
Operations
Every operation has a page of its own, with an example. All are POST with a JSON body as above, except where a
method is given.
Drawings
| Operation | Returns |
|---|---|
POST /inputs |
The template's labelled variables: what values can be given, of what kind. |
POST /drawing |
The complete drawing, as ParametrIQ drawing text, with the values given. |
POST /export/{format} |
The drawing as a file: PDF, SVG, EPS, AI, PNG, JPEG, WebP, G-code, HP-GL, CF2, DDES3, DXF or .piq, or a format for a particular machine or trade (dieline PDF, print-and-cut, GP-GL, LightBurn, laser and Cricut SVG, DSTV NC, AAMA/ASTM DXF, tiled print, projector PDF, mat cutter). |
POST /save/{format} |
The same file, saved to your storage rather than returned. |
POST /preview |
A PNG picture of the drawing, for a quick look. |
GET /formats |
The export formats, with their media types and extensions. |
Calculations
| Operation | Returns |
|---|---|
POST /variables |
The drawing's variables and their values, given and worked out. |
POST /evaluate |
Expressions worked out in the drawing (L*2, AREAOF(@1) …). |
POST /points |
The drawing's points: where each is, and how it is defined. |
POST /measure |
Its extent, its lines' lengths by layer, and its blanks' area and perimeter. |
POST /die |
The steel-rule die it needs, and an estimate of its cost. |
POST /fold |
The drawing folded in 3D: meshes to draw, at steps from flat to folded. |
GET /appearances |
The looks /fold can give a drawing: metals, boards, fabrics, plastics, wood … |
POST /pack-first |
Packs for a product: its arrangements in a case, each with a fitment and an outer from the library, costed and fitted to a pallet; and the drawing of one. |
Storages
| Operation | Does |
|---|---|
GET /storages |
Lists your storages. |
GET /storages/{name} |
One of them. |
PUT /storages/{name} |
Registers one, or replaces it. |
DELETE /storages/{name} |
Removes one. |
POST /storages/{name}/test |
Tries one with the keys kept for it. |
GET /storages/{name}/objects |
What is in one. |
Your storages
Templates can be kept in your own S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2, Wasabi, MinIO, DigitalOcean Spaces …), and exported files saved to it. Register a storage once, by a name of your choosing, then name it in requests.
PUT /storages/main
{
"serviceUrl": "https://ACCOUNT.r2.cloudflarestorage.com",
"region": "auto",
"bucket": "my-bucket",
"prefix": "piq/",
"accessKeyId": "…",
"secretAccessKey": "…"
}
- The storage is tried as it is registered: if it can't be reached, has no such bucket or refuses the keys, it isn't
registered, and the answer (
502) says why. Registering the same name again replaces it. prefixmakes a folder of the bucket the storage: keys are inside it, and can't reach above it.pathStyle(trueunless given) addresses objects as endpoint/bucket/key, as most services other than AWS want.- The endpoint must be
https, on the public internet. - Give keys that can only read and write that bucket (or prefix). The secret is kept encrypted, and never given back.
- You can have up to 10 storages.
In any drawing request, give source instead of template to read the template from your storage:
{
"source": { "storage": "main", "key": "templates/box.piq" },
"values": { "L": 500 }
}
Then save exported files to it with /save/{format}. The storage operations are listed
above.
Errors
A request that can't be carried out as asked is answered 400 Bad Request with a problem description: what is wrong,
by the value it is about.
{
"title": "One or more validation errors occurred.",
"status": 400,
"detail": "Some of the values given can't be used.",
"errors": {
"gt": [ "gt isn't one of the template's labelled variables: only those can be given values." ],
"l": [ "L is a number." ]
}
}
A template whose script goes wrong as it runs (a loop that never ends, say) is answered 400 too, the error
under template.
A storage that isn't yours or doesn't exist, or a key with nothing there, is answered 404. A key that can't be used
(one starting with /, with . or .. folders, or over 900 bytes) is answered 400. A file that is already
there is answered 409. A storage that can't be reached, has lost its bucket or refuses its keys is answered 502,
and one that doesn't answer in time 504.
A missing or unknown API key is answered 400 or 401.
Web addresses in templates
A template is a drawing, and a drawing can bring content in from outside: pictures, library parts and 3D models
linked to their files or web addresses, and IMPORT and
MERGE statements naming them. The service reads no files at all, and by default fetches no
web addresses either: a template that links or imports one is refused (400, saying which). Embed what a template
shows (Edit › Links › Embed All in ParametrIQ CAD) before sending it, and it needs nothing else.
Running the service yourself, you can allow web addresses from hosts you trust, in its configuration:
"SaaS": {
"UrlFetch": {
"AllowedHosts": [ "cdn.example.com", "*.assets.example.com" ],
"TimeoutSeconds": 20
}
}
*.example.com allows any host below example.com. Even from these hosts, only http and https addresses are
fetched, and only when every IP address the host's name resolves to is public: never a private (10.x, 172.16–31.x,
192.168.x, fc00::/7), loopback (127.x, 1), link-local (169.254.x, such as cloud metadata services, fe80/10),
shared or reserved address. This is checked for each redirect (at most five are followed) and again as the
connection is made, so a template can't make the service reach its own network. A source must arrive within
SaaS:UrlFetch:TimeoutSeconds (20), be at most 64 MB, and be of the right kind (an image for a picture, never a web
page); what can't be fetched refuses the template, saying why. What is fetched is cached (with the server's ETag
and Last-Modified) in the service's local application data.
Limits
- Time. Each request is given 20 seconds to run its template and make what is asked for. A template still running
then is stopped, and the request answered
422 Unprocessable Content: the same template with the same values would take as long again, so there is no point sending it again unchanged. A script is also stopped after a million statements, whatever the time. - At once. Only so many requests are worked on at once; the rest wait their turn. When too many are waiting,
more are turned away with
429 Too Many Requests: send it again a little later. - Size. A request is at most 8 MB, a template at most 2 million characters (one read from your storage at most 8 MB), and a picture at most 8000 pixels across.
Running the service yourself, storages need SaaS:Storage:EncryptionKey: 32 random bytes, base64
(openssl rand -base64 32). Keep it secret, in user secrets or an environment variable
(SaaS__Storage__EncryptionKey), not in appsettings.json. Changing it makes the kept secrets unreadable, so storages
must then be registered again. SaaS:Storage:AllowHttp and SaaS:Storage:AllowPrivateEndpoints (both false) let
storages use http and private addresses, for testing against a local MinIO. SaaS:Storage:MaxPerLicense (10) and
SaaS:Storage:TimeoutSeconds (30) and SaaS:Storage:MaxReadBytes (8 MB, the largest template read from a
storage) set the limits. Without the encryption key, registering or using a storage is answered 503.
Running the service yourself, these are set in its configuration: SaaS:TimeLimitSeconds (20),
SaaS:MaxConcurrent (the number of processors) and SaaS:QueueLimit (50).
See also: Clients (examples in JavaScript, Python, C#, Go and more), Run Parametric (the same templates, in ParametrIQ CAD).