Skip to content
Who Sees What

‹ Developers

Who Sees What REST API

The Who Sees What REST API runs the same audit operations the app runs, scoped to your connected org, on Who Sees What Enterprise. You create and revoke your own credentials in the app: open the organization menu and choose API access. Professional does not include API access; the same menu item shows a locked preview that points to the plans page.

The same credential also works with MCP for AI agents. This page is the narrative overview. The API reference is the complete, endpoint-by-endpoint reference: every route, every parameter and option, the exact request and response payloads, every error, and a Node and an Apex example for each call, including how to process what comes back.

Changed November 3, 2026. The reference now carries a Node and an Apex example for every endpoint, the full request and response anatomy, retry handling, webhook signature verification, and the complete template and slot catalog.

Access tiers

  • A Who Sees What Enterprise credential can call the /v1/whoseeswhat/* endpoints.
  • A call from a Professional org, or with a missing, invalid, expired, or revoked credential, returns 403 or 401.
  • Lynceon (coming soon) is a separate Digadop product and is not reachable through this API.

Getting a credential

There is no separate developer signup; you create a credential in the app itself.

  1. Be on the Enterprise edition for the org.
  2. Sign in as a user with Salesforce org-admin rights (Manage Users or Modify All Data).
  3. Open the organization menu, choose API access, then Create credential, with an optional label (up to 200 characters).
  4. The credential (a bearer token) is shown once, together with its expiry time. Copy it immediately: it cannot be shown again. The panel lists your credentials by label, created time, and last-used time, and lets you revoke one at any time. Revocation takes effect on the next request to the instance that served the revoke, and within 30 seconds everywhere.

The credential is a signed machine token scoped to the one org you created it in and to your edition at creation time. It expires one hour after it is created; the exact expiresAt is shown with the token. Plan for that: an unattended integration must create a fresh credential before each run (or have a person do so), and a 401 unauthorized after an hour is expiry, not a bug in your code. Use the same credential for both this REST API and MCP.

Base URL and authentication

The base URL is https://whoseeswhat.com, and the Who Sees What endpoints are under /v1/whoseeswhat. HTTPS is required. Every endpoint requires a bearer token:

Authorization: Bearer <your-token>

First call

List the audit templates your credential can run, then list the orgs you have connected, then run a template against one of them. Take the templateId from the template list and the orgId from the org list.

Node (18 or later, no dependencies):

const BASE = 'https://whoseeswhat.com';
const headers = { authorization: `Bearer ${process.env.WSW_TOKEN}` };

const templates = await (await fetch(`${BASE}/v1/whoseeswhat/templates`, { headers })).json();
const { orgs } = await (await fetch(`${BASE}/v1/whoseeswhat/orgs`, { headers })).json();

const run = await fetch(`${BASE}/v1/whoseeswhat/templates/wsw-007/runs`, {
  method: 'POST',
  headers: { ...headers, 'content-type': 'application/json' },
  body: JSON.stringify({ orgId: orgs[0].orgId, slots: { target: '0011a00000AbCdeAAC' } }),
});
const answer = await run.json();
console.log(answer.verdict, answer.summary);

Apex:

String wswToken = '<your credential>';   // better: a Named Credential or a Protected Custom Setting, never a literal in code
HttpRequest req = new HttpRequest();
req.setEndpoint('https://whoseeswhat.com/v1/whoseeswhat/templates/wsw-007/runs');
req.setMethod('POST');
req.setHeader('Authorization', 'Bearer ' + wswToken);
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(new Map<String, Object>{
    'orgId' => '00D000000000000EAA',
    'slots' => new Map<String, Object>{ 'target' => '0011a00000AbCdeAAC' }
}));
HttpResponse res = new Http().send(req);
Map<String, Object> answer = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
System.debug(answer.get('verdict') + ': ' + answer.get('summary'));

curl:

curl -s -X POST https://whoseeswhat.com/v1/whoseeswhat/templates/wsw-007/runs \
  -H 'authorization: Bearer <your-token>' -H 'content-type: application/json' \
  -d '{"orgId":"<orgId>","slots":{"target":"<recordId>"}}'

The run returns the explained answer in the same response: a verdict, a one-line summary, the evidence, and the access path behind it. There is no separate report to fetch.

Errors

Failures return a JSON envelope with a stable code:

{ "error": { "code": "forbidden", "message": "Not authorized for org 00D000000000000EAA." } }
StatuscodeMeaning
400invalid_inputThe request body or params failed the endpoint’s validation.
401unauthorizedMissing, invalid, expired, or revoked token.
403forbiddenAuthenticated, but not entitled to this endpoint or this org.
404not_foundNo such route.
405method_not_allowedThe path exists under a different method.
413invalid_inputThe JSON body exceeded 64 KiB.
429rate_limitedYou exceeded the rate limit; see below.
502upstream_errorThe backend call failed or timed out.
503auth_unavailableCredential verification could not answer; retry, do not re-authenticate.

Several endpoints answer 200 with a plain-language, non-error answer for conditions you might expect to be errors (a missing slot, an org that is not connected). Always read the response body, not just the status. The reference has the ordered behavior for each endpoint and a reusable error handler for Node and for Apex.

Reports and scheduled exports

Any question you can run over the API can also be exported as a report, in one call: POST /v1/whoseeswhat/reports returns the report inline as CSV, XLSX, PDF, or JSON, or as a short-lived signed link for a larger file. You can also list and create scheduled report exports (recurring runs delivered by email or to a signed webhook) under GET and POST /v1/whoseeswhat/schedules. Full request and response shapes, a Node and an Apex example for each, and how to verify a webhook delivery, are on the API reference page.

Rate limits

Every call is rate limited per credential (or per source IP for an unauthenticated request) at 120 requests per minute, with a global backstop of 600 requests per minute across all callers on the serving instance. Exceeding either returns 429 rate_limited with a Retry-After header, in seconds. This is separate from your own Salesforce org’s API limits (running a template or a search makes a real Salesforce API call on your behalf), and from the Who Sees What web app’s own per-org daily audit cap on interactive use, which does not apply to API or MCP calls.

Versioning

The API is versioned by URL path (/v1/whoseeswhat), with no separate version header. A future breaking change would ship under a new path prefix rather than changing an existing /v1 endpoint. There is no /v2 today.

Safety

  • Who Sees What scoping is enforced by the backend, so a script never exceeds the human caller’s own Salesforce access. Installing the auditor permission set is the setup step that writes to your org: it writes that permission set (and, if asked, assigns it to an existing user).
  • Who Sees What keeps its own records of what you ran: every call adds an audit record and a usage record in our systems. None of them change your Salesforce org.
  • Every response, success or error, carries an x-correlation-id header; quote it to support if you need help with a specific call.

Reference

The endpoint-by-endpoint reference, with a Node and an Apex example for every call, is on the API reference page.