Base URL https://grid.jipidy.com · authenticate with the X-API-Key header (find your key on the dashboard).
Renders a URL on every requested device. Full-page screenshot, console errors, load time, title and a visual-regression diff per device.
curl -X POST https://grid.jipidy.com/api/shot \
-H "X-API-Key: $JIPIDY_KEY" -H "Content-Type: application/json" \
-d '{
"url": "https://your-site.com",
"devices": ["desktop", "iphone-14"], // optional, default: 6-device grid
"diffThreshold": 0.1, // optional, % of pixels; 0 = strictest
"auth": { // optional, for protected apps — see Auth
"headers": {"Authorization": "Bearer ..."}
}
}'
| Field | Type | Notes |
|---|---|---|
url | string, required | public http(s) URL — private/internal IPs are rejected |
devices | string[] | desktop laptop iphone-14 iphone-se pixel-7 ipad galaxy-s9 safari iphone-14-safari ipad-safari firefox |
diffThreshold | number | diff "changed" cutoff in % of pixels (default 0.1) |
auth | object | headers / basic / cookies — see Auth section |
Runs a sequence of steps on every device. Named screenshot steps become visual-regression checkpoints. Returns HTTP 422 if any device fails — CI can just use curl -f.
curl -f -X POST https://grid.jipidy.com/api/flow \
-H "X-API-Key: $JIPIDY_KEY" -H "Content-Type: application/json" \
-d '{
"url": "https://your-site.com/en/ski-rental",
"devices": ["desktop", "iphone-14"],
"video": true, // optional: webm replay per device
"steps": [
{"fill": {"selector": "#location", "value": "Saalbach"}},
{"click": {"text": "SAALBACH"}},
{"waitFor": {"selector": "#shop-list", "timeout": 10000}},
{"screenshot": "shop-list"},
{"click": {"selector": "#shop-4675 [data-testid=proceedToShopLink]"}},
{"assertVisible": {"selector": "[data-testid=productGrid]"}},
{"screenshot": "products"}
]
}'
| Verb | Arguments | Does |
|---|---|---|
goto | url string | navigate (same SSRF rules as the start URL) |
click | {selector} or {text} | click first match |
fill | {selector, value} | fill input |
press | key string, e.g. "Enter" | keyboard press |
select | {selector, value} | select option |
waitFor | {selector, state?, timeout?} | wait for element (visible by default) |
scrollTo | {selector} | scroll element into view |
assertVisible | {selector} | fail step if not visible |
assertText | {selector, text} | fail step if text not contained |
screenshot | label string | full-page checkpoint + baseline diff |
Limits: 30 steps, per-step timeout ≤ 30 s, 120 s per device. Failed step ⇒ step log + failure screenshot + console errors in the response.
All three can be combined. Secrets are used for the run and never stored — not in the database, not in run history.
"auth": {
"headers": {"Authorization": "Bearer eyJ...", "X-Preview-Token": "abc"}, // ≤10 headers
"basic": {"username": "staging", "password": "hunter2"}, // HTTP basic auth
"cookies": [{"name": "session", "value": "s3cr3t", "domain": ".your-site.com"}]
}
Save a journey once, then re-run it by id, put it on a schedule, and get webhook alerts when it breaks or the UI changes. This is how you turn Jipidy into continuous monitoring for your site.
# save (upsert by name)
curl -X POST https://grid.jipidy.com/api/flows \
-H "X-API-Key: $JIPIDY_KEY" -H "Content-Type: application/json" \
-d '{
"name": "booking-journey",
"url": "https://your-site.com/en/ski-rental",
"devices": ["desktop", "iphone-14"],
"video": true,
"scheduleMinutes": 60, // run hourly (min 15; 0 = manual only)
"webhook": "https://hooks.slack.com/...", // POSTed on failure or visual change
"auth": {"headers": {"X-Preview-Token": "abc"}}, // stored AES-256-GCM encrypted
"steps": [ {"waitFor": {"selector": "#app"}}, {"screenshot": "home"} ]
}'
curl https://grid.jipidy.com/api/flows -H "X-API-Key: $JIPIDY_KEY" # list
curl -X POST https://grid.jipidy.com/api/flows/ID/run -H "X-API-Key: ..." # run + wait for result
curl -X DELETE https://grid.jipidy.com/api/flows/ID -H "X-API-Key: ..." # delete
# fire-and-forget: dispatch returns 202 immediately, then poll
curl -X POST https://grid.jipidy.com/api/flows/ID/dispatch -H "X-API-Key: ..."
# -> {"run": "9f2c1b...", "status": "queued", "poll": "https://grid.jipidy.com/api/runs/9f2c1b..."}
curl https://grid.jipidy.com/api/runs/9f2c1b... -H "X-API-Key: ..."
# -> {"status": "running"} ...then the full result once done
Webhook payload (only sent when something is wrong — flow-failed or visual-change):
{
"event": "flow-failed",
"flow": "booking-journey", "url": "https://your-site.com/en/ski-rental",
"run": "9f2c1b...", "ok": false, "changed": 0,
"failures": [{"device": "iphone-14",
"step": {"i": 3, "verb": "waitFor", "error": "Timeout 10000ms exceeded..."},
"failScreenshot": "https://grid.jipidy.com/shots/.../iphone-14--FAILED.png"}]
}
Scheduled runs count against your monthly quota like any other run; when quota is exhausted they pause until the next month. Stored auth is encrypted at rest and only decrypted at run time — it is never returned by any endpoint.
The first run of a URL (per device / per checkpoint label) creates the baseline automatically. Promote a later run to be the new baseline:
curl -X POST https://grid.jipidy.com/api/baseline \
-H "X-API-Key: $JIPIDY_KEY" -H "Content-Type: application/json" \
-d '{"run": "9f2c1b..."}'
GET /api/usage | current month usage vs quota |
GET /api/devices | device list + defaults |
GET /api/flow/verbs | flow verbs + limits |
GET /health | service health |
200 | run complete (shots: even if some devices failed — check results[].ok) |
422 | flow: at least one device failed a step |
401 | bad API key |
402 | monthly quota exceeded |
429 | more than 2 concurrent runs, or rate-limited |
400 | invalid input (bad URL, bad steps, SSRF-blocked target) |
visual-check:
stage: test
image: curlimages/curl
script:
- |
curl -f -s -X POST https://grid.jipidy.com/api/flow \
-H "X-API-Key: $JIPIDY_KEY" -H "Content-Type: application/json" \
-d "{\"url\": \"$REVIEW_APP_URL\", \"video\": true, \"steps\": [
{\"waitFor\": {\"selector\": \"#app\"}},
{\"screenshot\": \"home\"}
]}" | tee grid-result.json
artifacts:
paths: [grid-result.json]
when: always
The -f makes curl exit non-zero on HTTP 422 ⇒ the job fails when a journey breaks on any device.