List the workspace's open invitations
#!/usr/bin/env bashset -euo pipefailAPI=https://api.speccapture.comAUTH="Authorization: Bearer $SPECCAPTURE_API_KEY"
page=$(curl -sS --fail-with-body "$API/v1/workspace/invitations?status=pending" \ -H "$AUTH") || { echo "$page" >&2; exit 1; }
jq -r '.data[] as $invitation | "\($invitation.email) as \($invitation.role), invited by \($invitation.invitedBy // "someone"): its link works until \($invitation.expiresAt)"' <<<"$page"using System.Text.Json;
using HttpClient http = new() { BaseAddress = new Uri("https://api.speccapture.com") };http.DefaultRequestHeaders.Authorization = new("Bearer", Environment.GetEnvironmentVariable("SPECCAPTURE_API_KEY"));
JsonElement page = await Json(await Call(HttpMethod.Get, "/v1/workspace/invitations?status=pending"));
foreach (JsonElement invitation in page.GetProperty("data").EnumerateArray()) { Console.WriteLine($"{invitation.GetProperty("email")} as {invitation.GetProperty("role")}, invited by {invitation.GetProperty("invitedBy").GetString() ?? "someone"}: its link works until {invitation.GetProperty("expiresAt")}");}
// Sends a request; stops with the problem's code and detail if the API refuses it.async Task<HttpResponseMessage> Call(HttpMethod method, string path, HttpContent? body = null, string? idempotencyKey = null) { using HttpRequestMessage request = new(method, path) { Content = body }; if (idempotencyKey is not null) request.Headers.Add("Idempotency-Key", idempotencyKey); HttpResponseMessage response = await http.SendAsync(request); if (!response.IsSuccessStatusCode) { JsonElement problem = await Json(response); string? detail = (problem.TryGetProperty("detail", out JsonElement text) ? text : problem.GetProperty("title")).GetString(); Console.Error.WriteLine($"{(int)response.StatusCode} {problem.GetProperty("code")}: {detail}"); Environment.Exit(1); } return response;}
// Reads an answer as JSON.static async Task<JsonElement> Json(HttpResponseMessage response) => JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;import osimport sys
import requests
API = "https://api.speccapture.com"session = requests.Session()session.headers["Authorization"] = f"Bearer {os.environ['SPECCAPTURE_API_KEY']}"
def call(method, path, **kwargs): """Send a request; stop with the problem's code and detail if the API refuses it.""" response = session.request(method, API + path, **kwargs) if not response.ok: problem = response.json() sys.exit(f"{response.status_code} {problem['code']}: {problem.get('detail', problem['title'])}") return response
page = call("GET", "/v1/workspace/invitations?status=pending").json()
for invitation in page["data"]: print(f"{invitation['email']} as {invitation['role']}, invited by {invitation['invitedBy'] or 'someone'}: its link works until {invitation['expiresAt']}")const API = "https://api.speccapture.com";const headers = { Authorization: `Bearer ${process.env.SPECCAPTURE_API_KEY}` };
const page = await call("GET", "/v1/workspace/invitations?status=pending").then((response) => response.json());
for (const invitation of page.data) { console.log(`${invitation.email} as ${invitation.role}, invited by ${invitation.invitedBy ?? "someone"}: its link works until ${invitation.expiresAt}`);}
// Sends a request; stops with the problem's code and detail if the API refuses it.async function call(method, path, init = {}) { const response = await fetch(API + path, { ...init, method, headers: { ...headers, ...init.headers } }); if (!response.ok) { const problem = await response.json(); console.error(`${response.status} ${problem.code}: ${problem.detail ?? problem.title}`); process.exit(1); } return response;}Every invitation to join the workspace that is neither accepted nor withdrawn, newest first: who is invited (email), as what (role: admin or member), its status (pending, or expired: it can be sent again in the dashboard), who invited them, when it was made, when its current link was sent and emailed, and when that link expires. Never the link. An accepted invitation’s person is in GET /v1/workspace/members. Page with limit and cursor (the previous page’s nextCursor), narrow with createdAfter, createdBefore and status. A key acting as the workspace’s owner or an admin; invitations are made and withdrawn in the dashboard.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”How many items the page holds: 1 to 200, 50 when left out.
The previous page’s nextCursor, exactly as it came: the list continues just after that page’s last item, so items made or removed meanwhile never shift a page. Leave it out for the first page.
Only items made strictly after this date and time (ISO 8601, such as 2026-10-14T09:30:00Z; without an offset it is UTC).
Only items made strictly before this date and time (ISO 8601, such as 2026-10-14T09:30:00Z; without an offset it is UTC).
Only items with this status.
Responses
Section titled “Responses”OK
object
object
Bad Request: a problem with a stable code. See Errors for every code and what to do about it.
Every error: a problem with a stable code. https://speccapture.com/docs/api/errors lists every code.
object
A link to the code’s entry in the docs.
What went wrong, as a stable code to branch on: a published code never changes meaning.
This request’s trace: quote it when asking for help.
Forbidden: a problem with a stable code. See Errors for every code and what to do about it.
Every error: a problem with a stable code. https://speccapture.com/docs/api/errors lists every code.
object
A link to the code’s entry in the docs.
What went wrong, as a stable code to branch on: a published code never changes meaning.
This request’s trace: quote it when asking for help.