API Reference
Every OcaltQL script runs through one endpoint: POST https://ql.ocalt.com/api, authenticated with your identity/password and a q field containing the script. Full basics are on OcaltQL API & Client. This page goes further — every calling convention, and how to pass real request data through to a script running outside Site Mode.
identity is who you are. Your Ocalt username or the email address on your account — either one works, they identify the same namespace. It is not a display name and not something you choose per request; it is the account whose files, databases, machines and quota the script will run against.password field takes your API key. Not your account password. The key is on your account dashboard — the field is still named password so existing clients keep working, but the value should be the key. It authenticates identically, carries no sign-in rights, and rotating it invalidates the old one immediately without touching the password you sign in with. A leaked key costs one rotation; a leaked password costs the account.The Endpoint
| Field | Required | Description |
|---|---|---|
identity | Yes | Your Ocalt account email |
password | Yes | Your Ocalt account password |
q | Yes | The OcaltQL script to execute |
get | No | JSON-encoded object — populates !GET inside the script |
post | No | JSON-encoded object — populates !POST inside the script |
cookie | No | JSON-encoded object — populates !COOKIE inside the script |
path | No | String — populates !REQUEST('path') |
method | No | String — populates !REQUEST('method') |
Calling the API — Every Language
curl -X POST https://ql.ocalt.com/api \
-d "identity=your_identity" \
-d "password=your_api_key" \
-d 'q=EMIT "Hello World"'
# -c saves the session cookie, -b sends it back.
curl -s -c /tmp/jar -X POST https://ql.ocalt.com/api \
-d "identity=your_identity" -d "password=your_api_key" \
--data-urlencode $'q=START SESSION\nAFTER SET "who" AS "Kea" OF !SESSION'
curl -s -b /tmp/jar -X POST https://ql.ocalt.com/api \
-d "identity=your_identity" -d "password=your_api_key" \
--data-urlencode $'q=START SESSION\nAFTER EMIT !SESSION(\'who\')'
const fd = new URLSearchParams();
fd.append('identity', 'your_identity');
fd.append('password', 'your_api_key');
fd.append('q', 'EMIT "Hello World"');
// credentials:'include' lets the browser carry OQLSESSID across calls.
fetch('https://ql.ocalt.com/api', { method: 'POST', body: fd, credentials: 'include' })
.then(res => res.text())
.then(output => console.log(output));
// credentials:'include' carries OQLSESSID across calls automatically.
const call = (q) => fetch('https://ql.ocalt.com/api', {
method: 'POST', credentials: 'include',
body: new URLSearchParams({ identity: 'your_identity', password: 'your_api_key', q })
}).then(r => r.text());
await call('START SESSION\nAFTER SET "who" AS "Kea" OF !SESSION');
console.log(await call("START SESSION\nAFTER EMIT !SESSION('who')")); // Kea
const fd = new URLSearchParams({
identity: 'your_identity',
password: 'your_api_key',
q: 'EMIT "Hello World"'
});
const res = await fetch('https://ql.ocalt.com/api', { method: 'POST', body: fd });
console.log(await res.text());
// Node fetch doesn't persist cookies — capture Set-Cookie, resend it.
const API = 'https://ql.ocalt.com/api';
const creds = { identity: 'your_identity', password: 'your_api_key' };
const call = (q, cookie) => fetch(API, {
method: 'POST', headers: cookie ? { cookie } : {},
body: new URLSearchParams({ ...creds, q })
});
const r1 = await call('START SESSION\nAFTER SET "who" AS "Kea" OF !SESSION');
const jar = r1.headers.get('set-cookie')?.split(';')[0];
const r2 = await call("START SESSION\nAFTER EMIT !SESSION('who')", jar);
console.log(await r2.text()); // Kea
$response = file_get_contents('https://ql.ocalt.com/api', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query([
'identity' => 'your_identity',
'password' => 'your_api_key',
'q' => 'EMIT "Hello World"'
])
]
]));
echo $response;
<?php
$jar = tempnam(sys_get_temp_dir(), 'oql');
function ql($q, $jar) {
$ch = curl_init('https://ql.ocalt.com/api');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => $jar, CURLOPT_COOKIEFILE => $jar,
CURLOPT_POSTFIELDS => http_build_query([
'identity' => 'your_identity', 'password' => 'your_api_key', 'q' => $q,
]),
]);
$out = curl_exec($ch); curl_close($ch); return $out;
}
ql("START SESSION\nAFTER SET \"who\" AS \"Kea\" OF !SESSION", $jar);
echo ql("START SESSION\nAFTER EMIT !SESSION('who')", $jar); // Kea
import requests
response = requests.post('https://ql.ocalt.com/api', data={
'identity': 'your_identity',
'password': 'your_api_key',
'q': 'EMIT "Hello World"'
})
print(response.text)
import requests
s = requests.Session() # persists OQLSESSID automatically
creds = {'identity': 'your_identity', 'password': 'your_api_key'}
s.post('https://ql.ocalt.com/api',
data={**creds, 'q': 'START SESSION\nAFTER SET \"who\" AS \"Kea\" OF !SESSION'})
r = s.post('https://ql.ocalt.com/api',
data={**creds, 'q': "START SESSION\nAFTER EMIT !SESSION('who')"})
print(r.text) # Kea
using var client = new HttpClient();
var payload = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("identity", "your_identity"),
new KeyValuePair<string,string>("password", "your_api_key"),
new KeyValuePair<string,string>("q", "EMIT \"Hello World\"")
});
var response = await client.PostAsync("https://ql.ocalt.com/api", payload);
Console.WriteLine(await response.Content.ReadAsStringAsync());
using System.Net;
var handler = new HttpClientHandler { CookieContainer = new CookieContainer() };
using var client = new HttpClient(handler);
var creds = new Dictionary<string,string> {
["identity"] = "your_identity", ["password"] = "your_api_key" };
await client.PostAsync("https://ql.ocalt.com/api", new FormUrlEncodedContent(
new Dictionary<string,string>(creds) {
["q"] = "START SESSION\nAFTER SET \"who\" AS \"Kea\" OF !SESSION" }));
var res = await client.PostAsync("https://ql.ocalt.com/api", new FormUrlEncodedContent(
new Dictionary<string,string>(creds) {
["q"] = "START SESSION\nAFTER EMIT !SESSION('who')" }));
Console.WriteLine(await res.Content.ReadAsStringAsync()); // Kea
resp, _ := http.PostForm("https://ql.ocalt.com/api", url.Values{
"identity": {"your_identity"},
"password": {"your_api_key"},
"q": {`EMIT "Hello World"`},
})
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
api := "https://ql.ocalt.com/api"
creds := url.Values{"identity": {"your_identity"}, "password": {"your_api_key"}}
v1 := url.Values{"q": {"START SESSION\nAFTER SET \"who\" AS \"Kea\" OF !SESSION"}}
for k, x := range creds { v1[k] = x }
client.PostForm(api, v1)
v2 := url.Values{"q": {"START SESSION\nAFTER EMIT !SESSION('who')"}}
for k, x := range creds { v2[k] = x }
resp, _ := client.PostForm(api, v2)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body)) // Kea
require 'net/http'
uri = URI('https://ql.ocalt.com/api')
res = Net::HTTP.post_form(uri,
'identity' => 'your_identity',
'password' => 'your_api_key',
'q' => 'EMIT "Hello World"'
)
puts res.body
require 'net/http'
uri = URI('https://ql.ocalt.com/api')
http = Net::HTTP.new(uri.host, uri.port); http.use_ssl = true
creds = { 'identity' => 'your_identity', 'password' => 'your_api_key' }
r1 = http.post(uri.path, URI.encode_www_form(creds.merge(
'q' => %Q(START SESSION\nAFTER SET "who" AS "Kea" OF !SESSION))))
cookie = r1['set-cookie']&.split(';')&.first
r2 = http.post(uri.path, URI.encode_www_form(creds.merge(
'q' => "START SESSION\nAFTER EMIT !SESSION('who')")), { 'Cookie' => cookie })
puts r2.body # Kea
Passing Request Data Through the API
By default, the credentialed API carries no GET, POST, or request data at all — documented as a real boundary on Session & Cookies and Header & Globals. That boundary describes the default: nothing supplied, nothing available. It is not an absolute technical wall — any caller can opt in by supplying the same extra fields Site Mode's own router already sends internally: get, post, cookie, path, method, each JSON-encoded. Supply them, and !GET/!POST/!REQUEST/!COOKIE read from them exactly as if the call had come through Site Mode.
EMIT "Name: " & !POST('name')
AFTER EMIT "Search: " & !GET('q')
AFTER EMIT "Session cookie: " & !COOKIE('session')
// Inside your own Express route handler, forwarding the incoming request
const fd = new URLSearchParams();
fd.append('identity', 'your_identity');
fd.append('password', 'your_api_key');
fd.append('q', savedScript);
fd.append('get', JSON.stringify(req.query));
fd.append('post', JSON.stringify(req.body));
fd.append('cookie', JSON.stringify(req.cookies));
fd.append('path', req.path);
fd.append('method', req.method);
const res = await fetch('https://ql.ocalt.com/api', { method: 'POST', body: fd });
console.log(await res.text());
import requests, json
response = requests.post('https://ql.ocalt.com/api', data={
'identity': 'your_identity',
'password': 'your_api_key',
'q': saved_script,
'get': json.dumps(request.args.to_dict()),
'post': json.dumps(request.form.to_dict()),
'cookie': json.dumps(request.cookies.to_dict()),
'path': request.path,
'method': request.method,
})
print(response.text)
curl -s -b /tmp/jar -X POST https://ql.ocalt.com/api \
-d "identity=your_identity" -d "password=your_api_key" \
--data-urlencode "q=$saved_script" \
--data-urlencode 'get={"q":"search term"}' \
--data-urlencode 'post={"name":"Kea"}' \
--data-urlencode 'cookie={"session":"abc123"}' \
--data-urlencode "path=/profile" \
--data-urlencode "method=POST"
// A browser proxies its own live request context into the script.
const fd = new URLSearchParams({
identity: 'your_identity', password: 'your_api_key', q: savedScript,
get: JSON.stringify(Object.fromEntries(new URLSearchParams(location.search))),
cookie: JSON.stringify(Object.fromEntries(
document.cookie.split('; ').map(c => c.split('=')))),
path: location.pathname,
method: 'GET',
});
const res = await fetch('https://ql.ocalt.com/api', {
method: 'POST', body: fd, credentials: 'include' });
console.log(await res.text());
// Inside your Express route handler, forwarding the incoming request.
const fd = new URLSearchParams({
identity: 'your_identity', password: 'your_api_key', q: savedScript,
get: JSON.stringify(req.query),
post: JSON.stringify(req.body),
cookie: JSON.stringify(req.cookies),
path: req.path,
method: req.method,
});
const res = await fetch('https://ql.ocalt.com/api', { method: 'POST', body: fd });
res.text().then(out => console.log(out));
<?php
// Forward the end-user's incoming request to a saved OcaltQL script.
$ch = curl_init('https://ql.ocalt.com/api');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query([
'identity' => 'your_identity',
'password' => 'your_api_key',
'q' => $savedScript,
'get' => json_encode($_GET),
'post' => json_encode($_POST),
'cookie' => json_encode($_COOKIE),
'path' => $_SERVER['REQUEST_URI'] ?? '/',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
]),
]);
echo curl_exec($ch); curl_close($ch);
// Inside an ASP.NET controller, forwarding HttpContext.Request.
using var client = new HttpClient();
var payload = new FormUrlEncodedContent(new Dictionary<string,string> {
["identity"] = "your_identity",
["password"] = "your_api_key",
["q"] = savedScript,
["get"] = JsonSerializer.Serialize(Request.Query
.ToDictionary(k => k.Key, v => v.Value.ToString())),
["cookie"] = JsonSerializer.Serialize(Request.Cookies
.ToDictionary(k => k.Key, v => v.Value)),
["path"] = Request.Path,
["method"] = Request.Method,
});
var res = await client.PostAsync("https://ql.ocalt.com/api", payload);
Console.WriteLine(await res.Content.ReadAsStringAsync());
// Inside an http.HandlerFunc, forwarding the inbound *http.Request r.
getJSON, _ := json.Marshal(r.URL.Query())
resp, _ := http.PostForm("https://ql.ocalt.com/api", url.Values{
"identity": {"your_identity"},
"password": {"your_api_key"},
"q": {savedScript},
"get": {string(getJSON)},
"path": {r.URL.Path},
"method": {r.Method},
})
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
require 'net/http'
require 'json'
# Inside a Rack/Rails action, forwarding the incoming request.
uri = URI('https://ql.ocalt.com/api')
res = Net::HTTP.post_form(uri,
'identity' => 'your_identity',
'password' => 'your_api_key',
'q' => saved_script,
'get' => JSON.generate(request.GET),
'post' => JSON.generate(request.POST),
'cookie' => JSON.generate(request.cookies),
'path' => request.path,
'method' => request.request_method
)
puts res.body
identity/password/q still has empty !GET/!POST/!COOKIE, exactly as documented elsewhere. Supplying these extra fields is the caller's own explicit choice — typically a backend proxying real requests on behalf of its own end-users, one script execution per incoming request.