List the workspace's view links
#!/usr/bin/env bashset -euo pipefailAPI=https://api.speccapture.comAUTH="Authorization: Bearer $SPECCAPTURE_API_KEY"
page=$(curl -sS --fail-with-body "$API/v1/view-links?status=active" \ -H "$AUTH") || { echo "$page" >&2; exit 1; }
jq -r '.data[] as $link | "\($link.viewLinkId) \($link.fileName) sheet \($link.sheetNumber): opened \($link.openCount) times, works until \($link.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/view-links?status=active"));
foreach (JsonElement link in page.GetProperty("data").EnumerateArray()) { Console.WriteLine($"{link.GetProperty("viewLinkId")} {link.GetProperty("fileName")} sheet {link.GetProperty("sheetNumber")}: opened {link.GetProperty("openCount")} times, works until {link.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/view-links?status=active").json()
for link in page["data"]: print(f"{link['viewLinkId']} {link['fileName']} sheet {link['sheetNumber']}: opened {link['openCount']} times, works until {link['expiresAt']}")const API = "https://api.speccapture.com";const headers = { Authorization: `Bearer ${process.env.SPECCAPTURE_API_KEY}` };
const page = await call("GET", "/v1/view-links?status=active").then((response) => response.json());
for (const link of page.data) { console.log(`${link.viewLinkId} ${link.fileName} sheet ${link.sheetNumber}: opened ${link.openCount} times, works until ${link.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 read-only link the workspace has made - through the API or the dashboard - newest first: its sheet (jobId, sheetNumber, fileName), status (active, expired or withdrawn), createdAt, expiresAt, withdrawnAt, how often and when it was last opened, and who made it. Never its viewUrl: that is shown once, by the answer that made the link. A deleted job’s links are not listed. Page with limit and cursor (the previous page’s nextCursor), narrow with createdAfter, createdBefore and status. A live key acting as the workspace’s owner or an admin.
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
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.