Create a Site via API
Provision a disposable Joomla trial site programmatically and poll for its status using two simple REST endpoints.
Overview
Creating a site is asynchronous. The first endpoint (POST /api/v1/trial-sites) accepts your configuration and returns a siteId and pollToken immediately. Provisioning continues in the background. The second endpoint (GET /api/v1/trial-sites/{siteId}) lets you poll for status until the site is ready or failed.
Each trial site lasts as long as your account's site lifetime (4 hours by default) and is permanently deleted on expiry with no backup. The exact deletion time is in result.expiresAt.
Authentication
All requests require an API key sent as a Bearer token. Get your key at Settings → Application API Key (signed-in dashboard). One active key per account; the full key is shown once at creation.
Authorization: Bearer ij_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxContent-Type: application/jsonTreat the key like a password — it authorises site creation against your account quota. Rotate it from Settings if it is ever exposed.
- Missing, malformed, or revoked key →
401 Unauthorized. - One active key per account.
Base URL
All requests go to the production base URL https://jinstant.com. Every example in this guide uses it.
Endpoint 1 — Create a trial site
/api/v1/trial-sites202 AcceptedReserves a provisioning job and returns immediately. Every request body field is optional — send an empty JSON object ({}) or no body at all to use all defaults.
Request body
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
joomlaVersion | "5" | "6" | No | "6" | "5" provisions the latest Joomla 5 release (currently 5.4); "6" provisions the latest Joomla 6 release (currently 6.1). See the versions matrix below for allowed PHP combinations. |
phpVersion | "8.2" | "8.3" | "8.4" | No | "8.3" | Must be compatible with the selected Joomla version. Unsupported combinations return 400. |
Examples
curl -X POST "https://jinstant.com/api/v1/trial-sites" \ -H "Authorization: Bearer $JINSTANT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "joomlaVersion": "6", "phpVersion": "8.3" }'const res = await fetch("https://jinstant.com/api/v1/trial-sites", { method: "POST", headers: { Authorization: `Bearer ${process.env.JINSTANT_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ joomlaVersion: "6", phpVersion: "8.3" }),});const job = await res.json();// { siteId, pollToken, status, progressStage, progressMessage }$ch = curl_init("https://jinstant.com/api/v1/trial-sites");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("JINSTANT_API_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "joomlaVersion" => "6", "phpVersion" => "8.3", ]),]);$job = json_decode(curl_exec($ch), true);// siteId, pollToken, status, progressStage, progressMessagecurl_close($ch);Response — 202 Accepted
{ "siteId": "4db53b6f-b1a6-4e46-a793-9a15a9dbf9ef", "pollToken": "uG0Q6Zq4xv9M3v3q8c2m4mS0nLQh4x2M", "status": "queued", "progressStage": "queued", "progressMessage": "Your site is in line. We are setting up Joomla 6 / PHP 8.3."}| Name | Type | Description |
|---|---|---|
siteId | string (UUID) | Identifies the provisioning job. Use in endpoint 2's path. |
pollToken | string | Secret required to poll status. Pair it with siteId — store both. |
status | string | "queued" on creation. |
progressStage | string | Machine-readable stage. See the status lifecycle below. |
progressMessage | string | Human-readable progress text, safe to display in a UI. |
Endpoint 2 — Check status / get result
/api/v1/trial-sites/{siteId}?token={pollToken}200 OKRequires the same Authorization: Bearer key and the token query parameter. The job must belong to the authenticated user. Responses are always Cache-Control: no-store.
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
siteId | string | path | Yes | The siteId from endpoint 1. |
token | string | query | Yes | The pollToken from endpoint 1. Missing token → 400. |
Example request
curl "https://jinstant.com/api/v1/trial-sites/$SITE_ID?token=$POLL_TOKEN" \ -H "Authorization: Bearer $JINSTANT_API_KEY"Status lifecycle
The status field moves through these values while you poll:
| status | Terminal? | Meaning |
|---|---|---|
queued | No | Job accepted, waiting for a provisioning slot. |
provisioning | No | Actively building the Joomla environment. |
ready | Yes | Site is running. Credentials available on first read. |
failed | Yes | Provisioning failed. Check the "error" field. |
progressStage provides finer-grained machine-readable stages:queued → leasing_warm_instance → creating_files → starting_containers → waiting_for_joomla → configuring_routing → ready / failed.
Response examples
In progress
{ "status": "provisioning", "progressStage": "waiting_for_joomla", "progressMessage": "Almost there. Finishing setup for Joomla 6 / PHP 8.3.", "error": null, "result": null}Ready
{ "status": "ready", "progressStage": "ready", "progressMessage": "Joomla 6 / PHP 8.3 is ready.", "error": null, "result": { "slug": "ab3k9m2q8n", "siteUrl": "https://ab3k9m2q8n.jinstant.site", "expiresAt": "2026-06-20T21:05:00.000Z", "credentials": { "username": "admin", "password": "s3cr3t-one-time-pw", "administratorUrl": "https://ab3k9m2q8n.jinstant.site/administrator/", "magicLoginUrl": "https://ab3k9m2q8n.jinstant.site/administrator/instant-login-xxxx.php" } }}One-time credentials
The credentials block — password, administratorUrl, and magicLoginUrl — is returned only on the first poll that observes status: "ready". The server marks them consumed on that read. Subsequent polls still return result (slug, siteUrl, expiresAt) but with credentials: null.
Capture and store the credentials from the first ready response.
Failed
{ "status": "failed", "progressStage": "failed", "progressMessage": "Provisioning failed", "error": "Provisioning failed", "result": null}Response fields
| Name | Type | Description |
|---|---|---|
status | string | queued | provisioning | ready | failed |
progressStage | string | null | Fine-grained stage name. |
progressMessage | string | null | Human-readable status text. |
error | string | null | Populated when status is "failed". |
result | object | null | null until ready; the site details once ready (see below). |
result.slug | string | The site's subdomain label. |
result.siteUrl | string | Public URL of the running Joomla site. |
result.expiresAt | string (ISO 8601) | When the site auto-deletes (created + your account's site lifetime). |
result.credentials | object | null | Admin login details — returned once. null on subsequent polls. |
result.credentials.username | string | Admin username. |
result.credentials.password | string | Plaintext admin password. |
result.credentials.administratorUrl | string | Direct URL to the Joomla /administrator/ panel. |
result.credentials.magicLoginUrl | string | One-click auto-login URL — no password required. |
Polling strategy
- Poll endpoint 2 every ~2 seconds.
- Stop when
status === "ready"(capture credentials immediately) orstatus === "failed"(readerror). - Recommended timeout budget: 2–5 minutes. Most sites provision in under a minute when a warm-pool instance is available.
Complete example (Node.js)
This script creates a site, polls until it is ready, and logs the result. Credentials are captured on the first ready response.
const BASE = "https://jinstant.com";const KEY = process.env.JINSTANT_API_KEY; async function createTrialSite() { // 1) Create the job const create = await fetch(`${BASE}/api/v1/trial-sites`, { method: "POST", headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ joomlaVersion: "6", phpVersion: "8.3" }), }); if (create.status !== 202) throw new Error(`Create failed: ${create.status}`); const { siteId, pollToken } = await create.json(); // 2) Poll until ready or failed (≈5 min budget) const deadline = Date.now() + 5 * 60 * 1000; while (Date.now() < deadline) { const res = await fetch( `${BASE}/api/v1/trial-sites/${siteId}?token=${encodeURIComponent(pollToken)}`, { headers: { Authorization: `Bearer ${KEY}` } } ); const body = await res.json(); if (body.status === "ready") { // 3) Capture credentials NOW — they are returned only once. console.log("Site URL:", body.result.siteUrl); console.log("Login:", body.result.credentials); return body.result; } if (body.status === "failed") throw new Error(body.error ?? "Provisioning failed"); await new Promise((r) => setTimeout(r, 2000)); } throw new Error("Timed out waiting for the site to become ready.");}Site lifetime
Trial sites created via the API use your account's site lifetime, the same as sites created from the dashboard. It is 4 hours unless your account can change it. They are automatically and permanently deleted on expiry, with no backups, consistent with the Terms of Use. The exact deletion time is in result.expiresAt.
Versions reference
Joomla / PHP matrix
| joomlaVersion (API) | Provisions | Allowed phpVersion |
|---|---|---|
"5" | Joomla 5.4 | "8.2", "8.3" |
"6" (default) | Joomla 6.1 | "8.3" (default), "8.4" |
You choose the major line ("5" or "6") and always get the latest release we support on it. Unsupported combinations (e.g. joomlaVersion: "6" with phpVersion: "8.2") return 400 Bad Request.
Errors
All error responses use JSON: { "error": "message" }.
| HTTP | Meaning | Typical cause |
|---|---|---|
400 | Bad Request | Malformed JSON; unsupported joomlaVersion / phpVersion; unsupported combination; (endpoint 2) missing token. |
401 | Unauthorized | Missing, malformed, invalid, or revoked API key. |
404 | Not Found | No job matches that siteId + token for the authenticated user. |
409 | Conflict | Account already at its concurrent-site limit (default 3). Delete a site to free a slot. |
429 | Too Many Requests | More than 3 create attempts per 60-minute window from one IP. |
500 | Internal Server Error | Unexpected provisioning or server failure. |
Note
A malformed JSON body is rejected with 400 before authentication is checked. All other validation happens after the key is verified — so a bad key returns 401 even when the body is also invalid.
Rate limits & quotas
- Create rate limit: 3
POSTattempts per 60-minute window, per IP. Exceeding this returns429. - Concurrent-site quota:3 active sites per account (a site is "active" when its status is
queued,provisioning,ready,failed, ordeleting). Exceeding this returns409. Delete a site from your dashboard to free a slot.
Next steps
- Open your dashboard to manage sites and create an API key.
- Terms of Use — usage policy, site lifetime, and prohibited use.
- Privacy Policy.