Examples¶
Examples assume Central is reachable at https://app.example.test with empty BASE_PATH, and that PostgreSQL-backed auth is enabled. Replace secrets with values from your environment. Keep session cookies and PATs out of source control.
curl¶
Health¶
Password login and session cookie jar¶
COOKIE_JAR=$(mktemp)
curl -sS -c "$COOKIE_JAR" -b "$COOKIE_JAR" \
-X POST https://app.example.test/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=admin' \
--data-urlencode 'password=change-me' \
--data-urlencode 'next=/' \
-o /dev/null -w '%{http_code}\n'
curl -sS -b "$COOKIE_JAR" https://app.example.test/api/me | jq .
curl -sS -b "$COOKIE_JAR" https://app.example.test/api/catalog | jq .
curl -sS -b "$COOKIE_JAR" https://app.example.test/api/portal/cards | jq .
Create, use, and revoke a PAT¶
# requires an existing session in $COOKIE_JAR
curl -sS -b "$COOKIE_JAR" \
-H 'Content-Type: application/json' \
-d '{"name":"ci","expires_at":"2030-01-01"}' \
https://app.example.test/api/me/tokens | tee /tmp/pat.json
TOKEN=$(jq -r .secret /tmp/pat.json)
curl -sS -H "Authorization: Bearer $TOKEN" https://app.example.test/api/me | jq .
ID=$(jq -r .token.id /tmp/pat.json)
curl -sS -b "$COOKIE_JAR" -X DELETE "https://app.example.test/api/me/tokens/$ID" -w '%{http_code}\n'
Admin: create a portal card and upload a thumbnail¶
curl -sS -b "$COOKIE_JAR" -H 'Content-Type: application/json' \
-d '{
"title":"City basemap",
"card_type":"map",
"url":"/qgis-publish-service/maps/city",
"category":"Maps",
"is_public":true,
"sort_order":10
}' \
https://app.example.test/api/admin/portal/cards | jq .
curl -sS -b "$COOKIE_JAR" \
-F 'file=@./preview.png' \
https://app.example.test/api/admin/portal/thumbnails | jq .
Logout¶
curl -sS -c "$COOKIE_JAR" -b "$COOKIE_JAR" \
-X POST https://app.example.test/logout -o /dev/null -w '%{http_code}\n'
JavaScript (browser)¶
// Same-origin portal scripts should include credentials so acugis_session is sent.
async function loadPortal() {
const me = await fetch('/api/me', { credentials: 'include' }).then((r) => r.json());
const cards = await fetch('/api/portal/cards', { credentials: 'include' }).then((r) => r.json());
const catalog = await fetch('/api/catalog', { credentials: 'include' }).then((r) => r.json());
return { me, cards, catalog };
}
async function createToken(name) {
const res = await fetch('/api/me/tokens', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
});
if (!res.ok) throw new Error(await res.text());
return res.json(); // includes one-time secret
}
Form login from a browser still uses classic HTML form POST to /login (not JSON).
Python¶
import requests
BASE = "https://app.example.test"
s = requests.Session()
# Password login (same flow as acugis CLI)
r = s.post(
f"{BASE}/login",
data={"username": "admin", "password": "change-me", "next": "/"},
allow_redirects=True,
timeout=60,
)
r.raise_for_status()
me = s.get(f"{BASE}/api/me", timeout=60).json()
assert me.get("authenticated"), me
catalog = s.get(f"{BASE}/api/catalog", timeout=60).json()
cards = s.get(f"{BASE}/api/portal/cards", timeout=60).json()
created = s.post(
f"{BASE}/api/me/tokens",
json={"name": "notebook", "expires_at": "2030-12-31"},
timeout=60,
).json()
token = created["secret"]
# PAT-only client
pat = requests.Session()
pat.headers["Authorization"] = f"Bearer {token}"
print(pat.get(f"{BASE}/api/me", timeout=60).json())
# Admin group list (requires admin session)
groups = s.get(f"{BASE}/api/admin/groups", timeout=60)
groups.raise_for_status()
print(groups.json())
Go¶
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
)
func main() {
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
form := url.Values{}
form.Set("username", "admin")
form.Set("password", "change-me")
form.Set("next", "/")
resp, err := client.Post(
"https://app.example.test/login",
"application/x-www-form-urlencoded",
strings.NewReader(form.Encode()),
)
if err != nil {
panic(err)
}
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
meResp, err := client.Get("https://app.example.test/api/me")
if err != nil {
panic(err)
}
defer meResp.Body.Close()
var me map[string]any
json.NewDecoder(meResp.Body).Decode(&me)
fmt.Println(me)
body, _ := json.Marshal(map[string]any{
"title": "Ops dashboard",
"card_type": "dashboard",
"url": "/dashboard/",
"is_public": true,
})
req, _ := http.NewRequest(http.MethodPost, "https://app.example.test/api/admin/portal/cards", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
cardResp, err := client.Do(req)
if err != nil {
panic(err)
}
defer cardResp.Body.Close()
fmt.Println(cardResp.Status)
}
PHP¶
<?php
$base = 'https://app.example.test';
$cookieFile = tempnam(sys_get_temp_dir(), 'central');
function req($method, $url, $cookieFile, $body = null, $headers = []) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => $cookieFile,
CURLOPT_COOKIEFILE => $cookieFile,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
CURLOPT_FOLLOWLOCATION => true,
]);
$out = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [$code, $out];
}
[$code, ] = req(
'POST',
$base . '/login',
$cookieFile,
http_build_query(['username' => 'admin', 'password' => 'change-me', 'next' => '/']),
['Content-Type: application/x-www-form-urlencoded']
);
echo "login HTTP $code\n";
[, $me] = req('GET', $base . '/api/me', $cookieFile);
echo $me, "\n";
[, $catalog] = req('GET', $base . '/api/catalog', $cookieFile);
echo $catalog, "\n";
// Bearer example after creating a PAT in Central Account UI or via /api/me/tokens
$token = getenv('ACUGIS_TOKEN');
if ($token) {
$ch = curl_init($base . '/api/me');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
]);
echo curl_exec($ch), "\n";
curl_close($ch);
}
Common workflows¶
- Interactive operator:
POST /login→ browse/api/portal/cardsand/api/catalog→ open resource URLs. - Automation: create PAT in Account UI or
POST /api/me/tokens→ call APIs withAuthorization: Bearer. - Admin onboarding: create user → assign groups → set resource ACL matrix → optionally import discovery items as cards.
- CLI-oriented: use sibling
acugis login/acugis whoami/acugis catalog(see CLI).