Review Organization Proposal
Records your organization’s decision on a proposal that is waiting on you — one ChainPatrol escalated to you, or one on a report you submitted yourself. These are exactly the proposals your Review page shows and the ones GET /organization/reports?needsCustomerReview=true counts; find their IDs in proposals[].id on that endpoint. APPROVE blocks the asset and takes a label (plus a brandId for labels that name a specific brand). REJECT takes a rejectReason. WATCHLIST rejects the proposal and parks the asset so it is re-evaluated if it comes back alive.
Pass assetId alongside proposalId; it must match the proposal’s current asset or the request returns 409, which keeps a caller from actioning a proposal it never read.
Approvals are held to the same safety checks as the Review page, with no override: an asset that any enabled legitimacy rule marks as legitimate cannot be approved here, and an allowlisted asset cannot be blocked here. Both return 403 and must be handled on the Review page by a person.
Rate limited to 10 reviews per organization per day, since a review is not undoable and looping over a queue is easy to do by accident. Exceeding it returns 429. The limit is shared across all of the organization’s API credentials and does not apply to reviews made from the Review page.
Overview
Record your organization’s decision on a proposal that is waiting on you — one ChainPatrol escalated to you, or one on a report you submitted yourself. These are exactly the proposals your Review page shows and the onesGET /organization/reports?needsCustomerReview=true counts, so this endpoint lets you clear that queue without opening the web app.
There is one endpoint for all three outcomes, discriminated by decision:
WATCHLIST is a flavor of rejection rather than a separate outcome: it is stored as a REJECT review with the asset watchlisted, which is the same write the Review page performs. That is why a successful watchlist response comes back with review.decision: "REJECT" and watchlisted: true.
Quick Start
Authentication
Include your API key in theX-API-KEY header:
Finding a proposal to review
Both IDs this endpoint needs come from the reports listing. Ask for the proposals that are waiting on you, then readproposals[].id and proposals[].asset.id:
Example Request
Request Body
proposalId is a path parameter, so it is not repeated in the body.
Why assetId is required
assetId is an echo-back, not a lookup. The server compares it against the proposal’s current asset and returns 409 on a mismatch, which means a stale or mistyped proposalId fails loudly instead of reviewing a different asset than the one you read. Take both values from the same listing response and pass them together.
Block labels
label is required when approving and ignored otherwise. Brand Impersonation and Employee Impersonation also require brandId.
Reject reasons
Reject reasons feed ChainPatrol’s detection-tuning breakdown, so pick the closest one rather than putting the real reason innote:
Response
Success Response
Watchlist Response
A watchlist request is recorded as a rejection with the asset parked:Response Fields
Safety checks
Approving over the API is held to the same checks as the Review page, and unlike the UI they cannot be overridden:- Legitimacy rules. If any enabled legitimacy rule concluded the asset looks legitimate — a Tranco-listed domain, a regex match on a domain you own — approval returns
403. A person has to make that call on the Review page. - Allowlisted assets. An asset that is currently
ALLOWEDcannot be blocked here, also403.
Rate limit
Reviews are limited to 10 per organization per rolling 24 hours. The budget is shared across every API credential your organization has, and reviews made from the Review page do not count against it. Exceeding it returns429.
The limit exists because a review is not undoable and looping over a queue is easy to do by accident. Treat it as a backstop: if you are hitting it regularly, the queue is better cleared in the app.
Error Responses
400 Bad Request
A conditional requirement was not met, or the asset type cannot be watchlisted:403 Forbidden
The credential is not a customer member of the organization, or a safety check refused the write:404 Not Found
The proposal does not exist, or is not one of yours:409 Conflict
assetId does not match the proposal’s current asset, or the proposal has already been resolved:
429 Too Many Requests
The organization’s daily review budget is spent:Use Cases
Clear the queue that is waiting on you
Read the proposals waiting on your organization, then act on each one:Park an asset that went quiet
An asset that is dead or parked is not a false positive — watchlist it so ChainPatrol re-evaluates it if it comes back:From the CLI
The ChainPatrol CLI wraps this endpoint, and confirms before it writes:--yes to skip the confirmation in a script, or --dry-run to print the payload without submitting it.
Best Practices
Read before you write
Every request needsassetId from a listing you just read, which is deliberate. Fetch the proposal, decide from its asset and scan data, then review it — do not synthesize IDs or replay an old listing.
Treat 403 as routing, not failure
A403 from a safety check means the decision needs a person, not that your integration is broken. Log the proposal ID and move on; do not retry.
Do not retry a 409
A409 means either the asset does not match or the proposal is already resolved. Re-read the proposal instead of retrying with the same body.
Notes
- Reviews must be attributed to a customer-role member of the organization. Staff credentials are refused with
403. - Asset status is global: assets are unique on type and content, so approving blocks the asset everywhere rather than only for your organization.
- Not every asset type can be watchlisted. Types the watchlist cannot re-check are refused with
400instead of silently skipping the park. GET /organization/reportsreturnsproposals[].idspecifically so proposals can be addressed here.
Authorizations
Your API key. This is required by most endpoints to access our API programatically. Reach out to us at support@chainpatrol.io to get an API key for your use.
Path Parameters
ID of the proposal to review. Read it from proposals[].id on GET /organization/reports.
x > 0Body
Review a proposal on your organization's behalf
Records your organization's decision on a proposal ChainPatrol escalated to you,
or on a proposal belonging to a report you submitted yourself. These are exactly
the proposals your Review page shows, and the ones counted by
GET /organization/reports?needsCustomerReview=true.
ID of the asset this proposal is for, from proposals[].asset.id on GET /organization/reports. Required, and must match the proposal's current asset — a mismatch returns 409. This proves the request was built from a proposal you actually read, rather than from a guessed or stale ID.
x > 0What to do with the proposal. APPROVE blocks the asset, REJECT declines it, and WATCHLIST declines it but parks the asset so it is re-evaluated if it comes back alive. WATCHLIST is recorded as a rejection with the asset watchlisted — the same write the Review page performs.
APPROVE, REJECT, WATCHLIST Organization slug. Optional for organization-scoped API keys, which resolve the organization from the key itself. Required when your credentials can reach more than one organization.
Why the asset is being blocked. Required when decision is APPROVE, ignored otherwise. One of: Brand Impersonation, Employee Impersonation, Fake Employee, Targeting Org Users, General Phishing, C2 Server, False Positive, Organization Member Impersonation, Targeting Organization.
Brand Impersonation— The asset is directly using the Brand's name, logo, or other trademarksEmployee Impersonation— The asset is impersonating a specific employee of the organizationFake Employee— The asset claims to work at the company but is not directly impersonating a specific personTargeting Org Users— A scam the org wants taken down that falls outside direct impersonationGeneral Phishing— This asset is trying to steal user fundsC2 Server— Command and Control server providing backend infrastructure for phishing operationsFalse Positive— This asset was incorrectly flagged and should be allowedOrganization Member Impersonation— The asset is impersonating a member or employee of the organizationTargeting Organization— A scam the org wants taken down that falls outside direct impersonation
Brand Impersonation, Employee Impersonation, Fake Employee, Targeting Org Users, General Phishing, C2 Server, False Positive, Organization Member Impersonation, Targeting Organization Brand the asset impersonates. Optional for most labels, but required for Brand Impersonation and Employee Impersonation, which name a specific impersonation target. List your brands with GET /organization/brands.
x > 0Why the proposal is being rejected. Required when decision is REJECT, ignored otherwise. These are the same reasons the Review page offers, and they feed ChainPatrol's detection-tuning breakdown — pick the closest one rather than relying on note.
irrelevant, no_malicious_activity, insufficient_evidence, not_targeting_org, decayed Why the asset is being parked. Required when decision is WATCHLIST, ignored otherwise. Use OTHER for anything not covered, in which case note becomes required and is treated as the reason. Note that the watchlist only covers asset types that can be re-checked for revival (URL, PAGE, EMAIL, and the social platforms); watchlisting any other type is rejected, so reject the proposal instead.
DEAD, PARKING, NOT_ENOUGH_EVIDENCE, OTHER Free-text detail recorded on the review, alongside the structured reason. Required when watchlistReason is OTHER.
1000