REST API
Overview
Pensar provides a REST API for programmatic access to your workspace data. It covers the same pentest, issue, and fix capabilities as the MCP Server — plus attack-surface and webhook-endpoint routes that MCP does not expose — and is designed for automation, scripting, and building custom integrations.
If you want findings pushed to you rather than polled for, register a webhook endpoint instead of looping over GET /issues.
The REST API uses API key authentication. For AI-assistant integrations, consider using the MCP Server which supports OAuth-based authentication and works natively with tools like Claude, Cursor, and Windsurf.
Base URL
Authentication
All requests must include a valid Pensar API key. You can provide it in one of two ways:
The workspace is resolved automatically from the API key, so you do not need to send a workspace identifier. (The X-Workspace-Id header is only used by browser/WorkOS-JWT callers, not by API-key callers.)
Creating an API Key
- Navigate to Settings > Integrations > API Keys in the Pensar Console
- Click Create API Key
- Give it a descriptive name and copy the key value
- Store it securely — the key is only shown once
API keys are scoped to a workspace. All API requests authenticate against the workspace associated with the key.
Endpoints
Pentests
List Pentests
Returns all pentests (scans) in the workspace.
Response:
Get Pentest
Returns detailed information about a specific pentest.
Response:
Dispatch Pentest
Launch a new pentest in the workspace. The pentest is queued and runs asynchronously.
Request Body:
Response (201):
List Pentest Targets
Returns the targets tested during a pentest. A target is a single endpoint of the attack surface that the pentest exercised. Targets are the entry point for querying the pentest’s execution logs, which are persisted per target rather than per issue (see Target Logs).
Response:
Issues
List Issues
Returns security issues in the workspace with optional filtering.
Response:
issueLabel is the human-facing reference shown in the console and url links
straight to the issue. Every :issueId path below accepts either form.
closedAt and closedDisposition are null on an open finding. The list
carries the disposition because without it every closed finding reads as
resolved; the free-text close fields are on Get Issue only, since
this list is unpaginated.
Get Issue
Returns full details of a specific security issue, including description, CWE classification, proof-of-concept, and affected code location.
A closed issue also carries the full close record: closedAt,
closedDisposition, closedMethod (how it was closed — manual, retest,
mcp, api), closedReason, and closedComments. All five are null while
the issue is open.
Update Issue
Update the status of a security issue — close it, mark it as a false positive, or reopen it.
Request Body:
Response (200): the updated issue, including closedAt, closedReason, and closedDisposition.
closedDisposition is the structured verdict; closedReason is the free text
beside it. A finding closed without a disposition reads as Resolved
everywhere, so send one whenever the close is not a plain fix. Reopening clears
the disposition along with the rest of the close record, and false-positive is
its own status rather than a disposition, so it never carries one.
other is not accepted — it is being retired from the vocabulary. Any value
outside the four is a 400, including null, so omit the field rather than
sending one back from a read:
Retest Issue
Queues an asynchronous retest of a security issue against its original target.
Response (201):
The retest runs asynchronously. Rather than polling for the verdict, subscribe to the retest.completed webhook — it carries the verdict and the full issue.
List Issue Retests
Returns the issue’s retest history, most recent first. An issue that has never been retested returns an empty array, not a 404.
Response:
status is derived from the retest’s timestamps, error, and result rather than stored, so it cannot drift out of sync with them: queued, in-progress, fixed, still-vulnerable, or error. confidence is high, medium, or low, and is the field to build an escalation rule on.
Fixes
List Fixes
Returns all available auto-remediation fixes for an issue.
Get Fix
Returns the full fix details including the code diff, explanation, and affected file path.
Agent Logs
List Agent Logs
Returns agent execution logs for a specific issue.
Search Agent Logs
Search agent logs by text pattern with configurable context.
Request Body:
Target Logs
A pentest’s execution logs are persisted against the individual targets it
tested (see List Pentest Targets), not against the
issues it discovered. These endpoints query those logs directly by target id,
so you can inspect a target’s full pentest activity even when it produced no
issue. The level, role, and contextLines filters behave identically to
the issue Agent Logs endpoints.
List Target Logs
Returns agent execution logs for a specific pentest target.
Search Target Logs
Search a target’s agent logs by text pattern with configurable context.
Attack Surface
These endpoints expose the discovered attack surface of the workspace — the applications (apps) Pensar tracks and the individual endpoints within them. They have no MCP equivalent.
List Apps
Returns the applications in the workspace.
Create App
Creates a new application in the workspace.
Get App
Returns full details of a specific application.
Update App
Updates an application. All body fields are optional.
Delete App
Deletes an application.
List Endpoints
Returns the endpoints discovered for a specific application.
Create Endpoint
Adds an endpoint to an application.
Get Endpoint
Returns full details of a specific endpoint.
Update Endpoint
Updates an endpoint. All body fields are optional.
Delete Endpoint
Deletes an endpoint.
Search Apps
Searches applications in the workspace.
Search Endpoints
Searches endpoints across all applications in the workspace.
Webhooks
Register HTTPS endpoints that Pensar pushes signed findings to as they are created, source-mapped, retested, and closed. The payload contract, signature-verification snippets, retry semantics, and endpoint requirements are documented on the Webhooks page; this section is the route reference.
The path is /webhook-endpoints, not /webhooks. /webhooks/* is reserved for inbound provider receivers and returns 404.
Subscribable event names are issue.created, issue.updated, issue.status_changed, retest.completed, and pentest.completed.
List Webhook Endpoints
Returns the workspace’s webhook endpoints, newest first. The signing secret is never included.
Response:
The health block is updated on every delivery attempt, so this single call tells you whether a receiver is healthy. At 20 consecutive failures the endpoint is disabled automatically.
Create Webhook Endpoint
Registers an endpoint and mints its signing secret. The URL must be HTTPS, must use a hostname rather than an IP literal, and must resolve exclusively to publicly routable addresses — see Endpoint requirements. Duplicate entries in events are collapsed.
Response (201):
secret is returned by this route and by no other. Store it before you close the response — it cannot be read back, and rotating means deleting and re-registering the endpoint.
A URL Pensar would refuse to fetch is a 400 naming the reason; a URL already registered in the workspace is a 409.
Get Webhook Endpoint
Returns the endpoint plus its 20 most recent delivery attempts.
Response:
Update Webhook Endpoint
Updates an endpoint. All body fields are optional, but at least one must be supplied — an empty body is a 400. A changed url is re-validated. Setting enabled to true resets consecutiveFailures to zero, so a re-enabled endpoint does not trip the auto-disable threshold on its next failure.
Response:
Delete Webhook Endpoint
Deletes the endpoint and, by cascade, its delivery history.
Response:
Test Webhook Endpoint
Queues a real delivery so you can verify the wiring — URL reachability, signature verification, and your 2xx — without waiting for a real finding. It renders the workspace’s most recent finding as an issue.created event; if the workspace has no findings, it sends a clearly-marked synthetic one instead (label VULN-SAMPLE, nil UUID for every id). The event is always issue.created, whatever the endpoint subscribes to.
Response (202):
sample is true when the synthetic event was sent. The delivery is queued, not yet sent — read the delivery log for the outcome. Testing a disabled endpoint is a 409.
List Webhook Deliveries
Returns delivery attempts for an endpoint, newest first. One record per attempt, including retries.
Response:
status is pending, success, or failed. id is the value that travelled as the X-Pensar-Delivery header, and responseBody is truncated to 2 KB.
Auth
Validate API Key
Validates the API key and returns the associated workspace. Useful for CLI tools to resolve workspace context from a stored API key.
Response:
Error Handling
The API returns standard HTTP status codes. Error responses include a JSON body with an error field:
Other resource-specific variants include "Issue not found or not in this workspace" and "Fix not found or not in this workspace".
Endpoints taking an :issueId distinguish a malformed reference from an unknown
one. A value that is neither a UUID nor a VULN-… label is rejected before the
lookup runs:
A well-formed reference that matches no issue in the workspace returns 404.
Next Steps
Have findings pushed to your systems, signed, as they are created and source-mapped.
Connect AI assistants to Pensar via the Model Context Protocol. (The REST API is a superset — it also exposes attack-surface endpoints.)
Automate pentesting in your CI/CD pipeline.
Understand credit consumption for API-triggered pentests.
Set up your workspace and connect your first repository.