Firm dashboard with the workspace API
Build a screen for the firm's own hearings, deadlines and tasks from the workspace API, on a server, with a firm key and without exposing it.
Your firm already keeps its matters in Advocate OS. This guide builds a small page for a wall display or a partner's phone: today's hearings, deadlines this week and open tasks, from the workspace API.
It is the firm's own data. The key is the firm's own. It must stay on your server.
#1. Make a key for it
In Firm › Developers, make a live key named "dashboard" and give it only the scope workspace:read. A dashboard should not be able to add tasks.
Keep it in AOS_KEY on the server. Never send it to the browser: anyone who sees it can read the firm's matters.
A test key reads your firm's own workspace too, free, which is the right thing to build against first. Its writes are dry runs: they are checked as live and answered, but nothing is saved. Workspace reads cost ₹0, then ₹0.02 from 1,00,000 a month: each firm gets 1,00,000 free calls a month, shared by reads and writes.
#2. Three calls
const base = process.env.AOS_API_BASE!;
const headers = { Authorization: `Bearer ${process.env.AOS_KEY}` };
const get = async (path: string) => {
const res = await fetch(`${base}${path}`, { headers });
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.error.doc_url})`);
return body.data as Array<Record<string, string>>;
};
export async function dashboard(today: string, weekEnd: string) {
const [hearings, deadlines, tasks] = await Promise.all([
get(`/workspace/hearings?from=${today}&to=${today}`),
get(`/workspace/deadlines?to=${weekEnd}`),
get(`/workspace/tasks?limit=20`),
]);
return { hearings, deadlines, tasks };
}
import os, requests
BASE = os.environ["AOS_API_BASE"]
HEADERS = {"Authorization": f"Bearer {os.environ['AOS_KEY']}"}
def get(path, **params):
res = requests.get(BASE + path, headers=HEADERS, params=params, timeout=30)
body = res.json()
if not res.ok:
e = body["error"]
raise RuntimeError(f"{e['code']}: {e['message']} ({e['doc_url']})")
return body["data"]
def dashboard(today, week_end):
return {
"hearings": get("/workspace/hearings", **{"from": today, "to": today}),
"deadlines": get("/workspace/deadlines", to=week_end),
"tasks": get("/workspace/tasks", limit=20),
}
Dates are yyyy-MM-dd in India. Do not build them with toISOString(): it gives the UTC date, which is still yesterday between midnight and 05:30 IST. Format the IST date directly:
const ist = (d = new Date()) => new Intl.DateTimeFormat("en-CA", { timeZone: "Asia/Kolkata" }).format(d); // 2026-12-03
#3. Serve it from your own endpoint
The browser calls your server; your server calls the API.
import express from "express";
const app = express();
app.get("/api/dashboard", async (_req, res) => {
try {
const today = ist();
const weekEnd = ist(new Date(Date.now() + 6 * 86400000));
res.set("Cache-Control", "private, max-age=60");
res.json(await dashboard(today, weekEnd));
} catch (e) {
res.status(502).json({ error: String(e) });
}
});
Cache for a minute. A hearing list does not change second by second, and a page that polls the API on every refresh costs money and rate limit for nothing.
#4. Show it
For each hearing show the matter title, the court and court number, and the purpose. For each deadline show the date and how many days are left, with today's deadlines first. For each task show the title and the matter.
Link a row back to the matter in Advocate OS rather than rebuilding the matter screen. The workspace API is for reading and for small writes; the app is where the work happens.
#5. Add a task from the dashboard
If a partner taps "Remind me", add a task with a second key that has workspace:write:
curl -X POST https://adv.heliosynctech.com/api/v1/workspace/tasks \
-H "Authorization: Bearer $AOS_WRITE_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "matter_id": 1207, "title": "Call client before the hearing", "due_date": "2026-12-07" }'
The task shows on the matter's timeline marked as added through the API, so nobody wonders where it came from. Writes cost ₹0, then ₹0.10 from 1,00,000 a month and count toward the same free 1,00,000 calls. With a test key the call is a dry run: you get 201, "sandbox": true and an sbx_ id, and no task is made, so test the button freely.
#Things to get right
- One firm per key. The key sees one firm. Build one key per firm; do not share a key across firms.
- Clients who said no to AI are not in the results. Do not treat the list as complete for the firm's whole diary; use the app for that.
- Do not log document text. Matters hold client confidences.
- Revoke the key if the server is ever compromised: Firm › Developers › Revoke.