For AI agents: a documentation index is available at https://docs.coverbase.com/llms.txt. This page is also available in markdown by appending .md to the URL.
The Risk API gives you the same risk register, scenarios and reports the Risk page shows. A risk is an entry on the register with an inherent and a residual score, an owner, a scope and the signals (findings, contract gaps, alerts and other records) that support it.
Every endpoint is scoped to the organization of the API key. See API conventions for shared behavior.
The
/v1/risks, /v1/risk-exposure, /v1/risk-report, /v1/risk-config and /v1/risk-scenarios endpoints need the Risk module. When your organization does not have it, they return 404 with the code not_enabled.
Endpoints that need org_settings:read:org or org_settings:update:org work with a user API key. A service account key does not hold org_settings, so it receives 403 from them in an organization that enforces roles.
Archiving a risk or a scenario, and deciding on a risk the Coverbase agent proposed, stay decisions a signed-in user makes in the app. The API does not offer them.
Errors
Errors return adetail object with a code and a message.
Query the register
POST
POST /v1/risks/querystring[]
Any of
proposed, open, mitigating, accepted and closed.string[]
Risk domains to include.
string
Only risks scoped to this vendor.
string
Text to match in the risk description.
integer
default:"25"
Page size. The API caps it at 200.
integer
default:"0"
Rows to skip.
categories, owner_user_ids, owner_department_ids, unowned, appetite, source_types, institution_key, service_id, contract_id, finding_id, review_overdue, proposal_kinds and sort.
Response
Read the register summary and matrix
GET
GET /v1/risks/summaryGET
GET /v1/risks/matrixstring
Limit either view to one institution of a multi-entity organization.
string
For the matrix, limit to one risk domain.
Create a risk
POST
POST /v1/risks201 Created with the new risk and its computed scores.
string
required
What could happen.
string
The risk domain the risk belongs to.
integer
Inherent likelihood on your organization’s scale.
impact_level, residual_likelihood_level and residual_impact_level work the same way.string
The owning user. Use
owner_department_id to name a business unit instead.string[]
Vendors in scope.
service_ids, department_ids, entity_ids and region_codes set the rest of the scope.category, status, treatment, treatment_plan, review_at, accepted_until, institution_key and note.
Get or edit a risk
GET
GET /v1/risks/{risk_id}PATCH
PATCH /v1/risks/{risk_id}Attach or dismiss a signal
POST
POST /v1/risks/{risk_id}/signals201 Created with the attached signal.
string
required
One of
finding, contract, contract_clause, contract_component_gap, obligation, screening_match, radar_signal, radar_alert, vendor_risk_change, inspect_evaluation, assessment and supplier_site.string
required
The ID of the record.
string
Why the record supports this risk.
POST
POST /v1/risks/{risk_id}/signals/{signal_id}/dismissRead exposure, the board report and the configuration
GET
GET /v1/risk-exposureinstitution_key to limit the domain gauges to one institution.
GET
GET /v1/risk-reportperiod with a key from the response’s periods to read an earlier period.
GET
GET /v1/risk-configRun and save scenarios
POST
POST /v1/risk-scenarios/runobject
required
The scenario. Set
kind to event, change or offboard, name the subject with subject_vendor_id or subject_node_id, or send a plain-English prompt such as “Okta is down for 24 hours”.POST
POST /v1/risk-scenarios201 Created with the saved scenario. Send inputs, an optional title, and the result of a run. When you omit result, Coverbase runs the scenario once and saves what it returns, which also needs risk:run:org.
GET
GET /v1/risk-scenarioslimit (at most 200) and offset.
GET
GET /v1/risk-scenarios/{scenario_id}Manage risk domains
GET
GET /v1/risk-domainslimit (at most 200) and offset. Pass include_archived=true to include archived domains.
POST
POST /v1/risk-domains201 Created with the new domain. Coverbase records the change on the domain’s activity feed.
string
required
The domain’s name, 1 to 255 characters.
string
What the domain covers, up to 1000 characters.
number
The domain’s weight in the weighted roll-up, from 0 to 100.
string[]
Users who review the domain. Use
department_reviewer_ids for user groups.PATCH
PATCH /v1/risk-domains/{risk_domain_id}null for weight takes the domain out of the roll-up. To archive a domain, a signed-in user does it in the app.
Read a vendor’s risk score history
GET
GET /v1/vendors/{vendor_id}/risk-score-historylimit (at most 200) and offset. An entry with a null risk_domain_id is the vendor’s overall score.
Read methodology and AI governance
These reads need the TPRM lifecycle. When your organization does not have it, they return404 with the code not_enabled.
GET
GET /v1/risk-methodologyirq_aggregation_method, which decides how inherent risk questionnaire answers aggregate into a score.
GET
GET /v1/reviewer-matrix-rulesdata. Each rule names a requester group, risk level, risk domain and business unit (a null criterion matches anything) and one reviewing user or group.
GET
GET /v1/work-queue-escalation-rulesdata.
GET
GET /v1/ai-settingsGET
GET /v1/agent-actionsawaiting_decision, target_type and target_id, and page with limit (at most 200) and offset. Deciding an action stays with a signed-in user.
Read access removal requests
GET
GET /v1/vendors/{vendor_id}/access-removal-requests404 when Inspect is not enabled. Approving or rejecting a request stays with a signed-in user.
Change configuration and methodology
PATCH
PATCH /v1/risk-config422 with the code invalid_risk_config.
PUT
PUT /v1/risk-methodologyirq_aggregation_method and returns it.
POST
POST /v1/reviewer-matrix-rules201 Created with the new rule. Send requester_department_id, risk_level_id, risk_domain_id and business_unit_id (a null criterion matches anything) and one of reviewer_user_id or reviewer_department_id. PUT /v1/reviewer-matrix-rules/{rule_id} replaces a rule with the same body.
POST
POST /v1/work-queue-escalation-rules201 Created with the new rule. Send name, overdue_days (0 to 365), target_type, item_types (empty covers every type), target_user_id or target_department_id, and reassign. PUT /v1/work-queue-escalation-rules/{rule_id} replaces a rule with the same body.
PATCH
PATCH /v1/ai-settingspii_redaction_enabled, learning_enabled, evaluation_guidance (null clears it) or policies, and returns the settings. A policy that always needs a person, or that no agent enforces yet, returns 400 with the code policy_not_allowed.
GET /v1/ai-settings/learned-examples lists the examples the org’s reviewers taught control evaluation, newest first.
Other reads and actions
POST /v1/risks/{risk_id}/commitment-requests asks a risk’s owner to commit to treating it. Send an optional due_at and message. It returns 201 Created with the finding_id and commitment_id it created.
POST /v1/risk-chain/query returns the dependency chain. Send the same filters the Risk page uses, such as follow, depth, critical_only and focus_node_id.
GET /v1/access-removal-requests/{request_id} returns one access removal request.
GET /v1/vendors/{vendor_id}/due-diligence-status returns is_current, the latest completed assessment and the next assessment date. It needs the TPRM lifecycle.
GET /v1/findings-stats returns the Findings page’s insight numbers over every finding.
POST /v1/commitments/{commitment_id}/extend-due-date moves a mitigation-underway commitment’s due_at later and stores an optional internal note. The vendor’s acceptance stands. A commitment that is not mitigation-underway, or a date that is not later, returns 400 with the code extension_not_allowed.
Questionnaire versions
These need the TPRM lifecycle.GET /v1/questionnaire-versions lists versioned templates; pass types (default irq). POST /v1/questionnaires/{questionnaire_id}/draft-version returns 201 Created with a new draft copied from that version. POST /v1/questionnaires/{questionnaire_id}/publish-version publishes a draft so new sends use it.
Responsibilities
These need Responsibilities enabled for your organization, and return404 with the code not_enabled otherwise.
GET /v1/responsibility-topics lists the topics in template order. POST /v1/responsibility-topics returns 201 Created with a new topic. Send label, question, and optionally guidance, default_iaas, default_paas, default_saas and owner_department_id.
GET /v1/vendors/{vendor_id}/responsibility-matrix returns the vendor’s matrix, its current version and its version history. Pass engagement_id for an engagement’s matrix. It returns 404 with the code responsibility_matrix_not_found until a matrix is saved.
POST /v1/vendors/{vendor_id}/responsibility-matrix saves columns, rows, cells and an optional note as a new published version and returns the matrix.
Attestations and review sessions
GET /v1/questionnaires/{questionnaire_id}/responses/{response_id}/attestation returns the questionnaire’s attestation config, every record on the response, and is_satisfied. Signing an attestation stays with a person.
GET /v1/assessments/{assessment_id}/review-sessions lists the remote and onsite review sessions on an assessment. It needs the TPRM lifecycle.
Work queue items
The work queue holds tasks assigned to people, such as reviewing an assessment. A key reaches the items assigned to its own user; an admin user key reaches every item.GET /v1/work-queue-items lists items, newest first. Filter with statuses, types, vendor_id and assessment_id, and page with limit (at most 200) and offset. GET /v1/work-queue-items/{work_queue_item_id} returns one item. An item the key cannot reach returns 403 with the code work_queue_item_forbidden.
POST /v1/work-queue-items/{work_queue_item_id}/complete marks an item complete. An item a record owns resolves with that record and returns 409 with the code work_queue_item_conflict. POST /v1/work-queue-items/{work_queue_item_id}/reopen puts a completed item back to pending.
POST /v1/work-queue-items returns 201 Created with a new item and notifies its assignee. Send title, description, instructions, url, vendor_id and assigned_user_id, and optionally type, assessment_id (an assessment of the same vendor) and due_at. An unknown vendor, user or assessment returns 400 with the code invalid_work_queue_item.
PATCH /v1/work-queue-items/{work_queue_item_id} edits an item. Fields you omit keep their value, and an explicit null clears due_at or assessment_id. A new assignee is notified.
POST /v1/work-queue-items/reassign moves work_queue_item_ids to assignee_id, a user or a round-robin user group. A round-robin group spreads the items across its members. An assign-to-all group returns 422. The response gives reassigned_count and the assignee.
Finding acceptance rules
Acceptance rules decide who recommends, who decides and who approves when a finding’s risk is accepted. The first rule that matches a finding’s severity routes it. The rule endpoints need the TPRM lifecycle and return404 with the code not_enabled otherwise.
GET /v1/finding-acceptance-rules lists the rules in the order they are checked.
POST /v1/finding-acceptance-rules returns 201 Created with a new rule. Send name, and optionally match_level_ids (severity level IDs; empty matches every severity), recommender_department_id, decider_department_id, extended_acceptance_days with extended_approver_user_id, and sort_order. Without sort_order the rule is checked last. A user group or approver that does not exist, or a threshold sent without an approver, returns 400 with the code invalid_rule.
PUT /v1/finding-acceptance-rules/{rule_id} replaces a rule with the same body. A field you leave out is cleared, so send every value the rule should keep. PUT /v1/finding-acceptance-rules/order takes rule_ids, every live rule in the new order.
GET /v1/findings/{finding_id}/decision-path returns where a finding stands: phase, the routing rule in route, the decisions so far, and can_recommend, can_decide and can_approve for the key’s user. Without the TPRM lifecycle it returns enabled: false.
Recording a decision and accepting a risk stay with a signed-in user in the app, and archiving a rule does too. The API does not offer them.
More Risk module reads and actions
These need the Risk module, like the other/v1/risk* endpoints.
GET /v1/risks/{risk_id}/signal-suggestions lists findings, contract gaps, alerts and other records that look related to a risk and are not attached yet. Nothing is attached.
GET /v1/risk-chain/nodes/{node_id} returns one chain node: what it depends on, what it serves, contract terms, and the open findings, obligations and risks on it. Pass snapshot_id when you have it. An unknown node returns 404 with the code not_found. POST /v1/risk-chain/rebuild rebuilds the chain from current records and returns it.
POST /v1/risk-chain/lineage returns one row per business unit, service and vendor path, with its fourth party hops. Send optional data_class_ids and department_ids. It also needs TPRM expansion.
POST /v1/risk-geography/query groups supplier locations by country or region. Send group_by, an optional focus_country, and an optional vendor filter in the shape the Vendors page uses.
GET /v1/risk-scenarios/subjects lists the chain nodes a scenario can run against. Filter with search. POST /v1/risk-scenarios/{scenario_id}/attach links a saved scenario to risk_id. POST /v1/risk-scenarios/create-risks returns 201 Created with the risk_ids it added from a scenario’s drafts.
POST /v1/risk-ask answers a question about the register from your records, with citations. Send earlier turns in history. Nothing is saved.
POST /v1/risk-agent/run runs the risk agent now. It refreshes signals, attaches new ones and proposes risks. A person decides each proposal in the app.
GET /v1/risk-narrative returns the saved narrative for a report_type and optional scope_key or subject_id, or null when none is saved. PUT /v1/risk-narrative saves an edited content and marks it as edited by a person. PUT /v1/risk-narrative/instructions saves custom_instructions for the next generation.
More methodology and AI governance
These need the TPRM lifecycle. Theorg_settings endpoints need a user key, as described above.
GET /v1/vendor-tier-rule returns the vendor tier rule, whether chain inputs are available, and the user group that approves a tier move. POST /v1/vendor-tier-rule/preview returns every vendor a rule would move, and changes nothing. PUT /v1/vendor-tier-rule saves the rule and then moves each vendor it changes; a null rule turns tiering off. The tier rule endpoints also need TPRM expansion.
GET /v1/risk-methodology/scale-normalization shows how each risk domain’s control scale maps onto your risk levels, and the residual risk formula.
POST /v1/reviewer-matrix-rules/reassign gives every rule in rule_ids the same reviewer_user_id or reviewer_department_id, and returns updated_count.
GET /v1/work-queue-escalation-log lists every escalation the rules made, newest first. Filter with escalation_rule_id and page with limit and offset. GET /v1/work-queue-items/{work_queue_item_id}/routing-events shows how one item was routed, reassigned and escalated.
GET /v1/agent-actions/{action_id} returns one Agent Ledger entry. GET /v1/agent-decision-rates returns how often people accepted, edited or rejected what Coverbase AI proposed, per playbook, over the last days (1 to 365).
PATCH /v1/ai-settings/learned-examples/{example_id} turns one learned example on or off with is_enabled. POST /v1/ai-settings/learned-examples/harvest picks up reviewer overrides now instead of at the nightly harvest, and returns the examples.
Control links, risk floors and what-if scores
GET /v1/risk-domain-control-links lists each risk domain’s linked control sets and sections. Pass include_archived=true to include archived domains. PUT /v1/risk-domains/{risk_domain_id}/control-links replaces a domain’s links with control_set_ids and control_section_ids. A set or section that does not exist returns 400 with the code invalid_control_links.
GET /v1/vendors/{vendor_id}/residual-risk-floor and GET /v1/services/{service_id}/residual-risk-floor return the significant deficiency that holds residual risk at a minimum, or floor: null.
POST /v1/questionnaire-responses/{response_id}/irq-what-if returns where an inherent risk questionnaire’s score would land with some answers changed. Send changes, one question_id with its option_ids per answer. POST /v1/assessments/{assessment_id}/residual-what-if returns where an assessment’s residual risk would land with the issues in remediate_eval_ids remediated. Both need the TPRM lifecycle and save nothing.
Questionnaire versions and attestation settings
GET /v1/questionnaires/{questionnaire_id}/change-log lists who drafted, published, reviewed or retired each version of a template, newest first. GET /v1/questionnaires/{questionnaire_id}/version-diff compares a version with base_questionnaire_id. Without a base it compares with the published version, or with the version before when this one is published.
PUT /v1/questionnaires/{questionnaire_id}/content-review sets interval_months and owner_user_id for content reviews; a null interval_months removes the cadence. POST to the same path records that the content was reviewed today. These need the TPRM lifecycle.
PUT /v1/questionnaires/{questionnaire_id}/attestation-config sets what a questionnaire asks a respondent to attest to. Records already signed keep the statement they were signed against. An invalid config returns 400 with the code invalid_attestation_config.
Retiring a version stays with a signed-in user in the app.
More responsibilities
These need Responsibilities enabled.PATCH /v1/responsibility-topics/{topic_id} edits a topic. Fields you omit keep their value, and an explicit null clears guidance or owner_department_id. PUT /v1/responsibility-topics/order takes topic_ids, every live topic in the new order.
POST /v1/vendors/{vendor_id}/responsibility-matrix/generate starts AI Autofill and returns 202 Accepted with a version in the generating state. To get the result, poll GET /v1/vendors/{vendor_id}/responsibility-matrix/versions/{version_id} until its status is draft or failed, then publish the draft with POST .../versions/{version_id}/publish. When the job cannot start, the version is marked failed and the call returns 503 with the code generation_not_started.
POST .../versions/{version_id}/discard deletes a draft that is not current. A service account key receives 403 with the code api_key_not_allowed. POST /v1/vendors/{vendor_id}/responsibility-matrix/revert copies target_version_id into a new published version. Each of these takes the same engagement_id query parameter as the matrix.
PUT /v1/vendors/{vendor_id}/engagements/{engagement_id}/service-model sets the engagement’s service_model and fills every topic no document has settled with that model’s default. GET .../responsibility-acknowledgements shows where each requested topic stands with its owner group. GET .../unlinked-cuecs lists the vendor’s complementary user entity controls that no topic cites. GET .../rights returns the results of a linked assessment, such as a contractual rights review; pass assessment_id to pick one. POST .../rights/scope-to-contracts points the assessment_id at the engagement’s contract documents only.
Findings, commitments, obligations and assessments
GET /v1/findings/{finding_id}/related-findings lists the same third party’s other open findings, newest first, and needs the TPRM lifecycle. GET /v1/findings/{finding_id}/jira returns whether Jira is connected and the ticket linked to the finding. GET /v1/findings/{finding_id}/jira/draft drafts a Jira ticket from the finding for you to edit and sends nothing. Both need TPRM expansion, and the draft also needs a connected Jira.
POST /v1/commitments/{commitment_id}/regenerate-review runs the AI review of the vendor’s latest round again, with your current review instructions, and returns 202 Accepted. When the latest round is not awaiting review it returns 400 with the code review_not_pending.
POST /v1/obligations/bulk-action applies one action to every ID in obligation_ids and returns 204 No Content. The actions are change_assignee (with assignee_id, a user or user group), change_status (with status), set_due_date (with due_date, or null to clear it), set_controls (with control_ids) and delete, which archives. A service account key cannot use delete. Unknown IDs are skipped.
POST /v1/obligations/extract-from-documents queues a job that reads vendor_document_ids of vendor_id and adds the obligations it finds. It returns 202 Accepted with document_count. Optionally send obligation_type, service_ids, assignee_id and due_date for every obligation it adds. The key’s user is emailed when the job finishes.
PUT /v1/assessments/{assessment_id}/method sets assessment_method, such as remote or onsite. It needs the TPRM lifecycle.
POST /v1/work-queue-items/complete completes every pending item in work_queue_item_ids, or every item filter and search match. Items the key cannot reach and items a record owns stay pending. It returns completed_count.
Review sessions
POST /v1/assessments/{assessment_id}/review-sessions returns 201 Created with a new remote or onsite session. Send title, mode (remote or onsite), scheduled_at, and optionally duration_minutes, location, notes, participant_user_ids and participant_emails. Participant users are notified in the app, and the vendor’s attendees in participant_emails are emailed. A time more than a day in the past returns 400 with the code invalid_review_session. These need the TPRM lifecycle.
PUT /v1/assessments/{assessment_id}/review-sessions/{review_session_id} replaces a session with the same body. When the time or place moves, every participant is told again. Otherwise only the people you added are told. Cancelling a session stays with a signed-in user in the app.
Ask for responsibility acknowledgements
POST /v1/vendors/{vendor_id}/engagements/{engagement_id}/responsibility-acknowledgements asks each owner group to acknowledge its topics. Send the matrix row_ids, a due_at, and an optional description. Each group gets one Obligations Portal and one email that lists only its topics. The response gives each topic’s status, emails_sent, groups_without_members (groups with nobody to email) and groups_not_emailed (groups whose email failed; their requests are saved). A selection that cannot be requested returns 422 with the code invalid_acknowledgement_request. It needs Responsibilities.
Attestation documents
GET /v1/questionnaires/{questionnaire_id}/responses/{response_id}/attestation/documents/{org_document_id} returns a url that downloads a document the questionnaire asks respondents to read, and its filename. The URL expires after five minutes, and the response is never cached. A document the questionnaire does not name returns 404.
Extract findings from a document
To read findings out of an org or vendor document, start a run and poll it.POST /v1/documents/{document_id}/finding-extraction returns 202 Accepted with status: pending. Send optional extra_instructions, added after your organization’s saved extraction instructions, and for a vendor document an optional assessment_id it must be linked to. A document that does not exist returns 404 with the code document_not_found. A system-generated assessment report, or a vendor document not linked to assessment_id, returns 422 with the code extraction_not_allowed.
GET /v1/documents/{document_id}/finding-extraction returns the run’s status (pending, running, completed or failed), the candidates it found with their quotes and locations, and the accepted_candidates.
POST /v1/documents/{document_id}/finding-extraction/accept records candidates you accept. Send each one’s candidate_id and title, and optionally an edited description, finding_severity_level_id, finding_urgency_level_id, finding_risk_level_id and due_date.
Exports and imports
Exports run in the background. Each start call returns202 Accepted with the export’s id and status: queued. Poll the matching GET .../exports/{export_id} until status is complete; the response then carries a download_url that expires after expires_in seconds. An ID from a different kind of export returns 404 with the code export_not_found.
POST /v1/findings/exports?file_name=... writes an Excel workbook of the findings the optional filter, search and sort in the body match.
POST /v1/risk-chain/lineage/exports writes the data lineage to a file. Send export_filename, format (xlsx or csv), and optional data_class_ids and department_ids. It needs the Risk module and TPRM expansion.
POST /v1/agent-actions/exports writes a CSV of the Agent Ledger actions an optional filter matches. It needs the TPRM lifecycle and a user key.
POST /v1/responsibility-topics/import creates or updates topics from an uploaded workbook, sent as multipart form data with file and an optional dry_run. Rows match existing topics by name. With dry_run=true nothing is saved, and the response shows what would change. A workbook that cannot be read returns 422. It needs Responsibilities and a user key.
AI narratives and suggestions
The app writes these texts as you watch. Through the API you start a job and poll it, since the model takes a few seconds to answer. Each start call returns202 Accepted with the job’s id, its kind and status: pending. Poll GET /v1/text-jobs/{job_id} until status is completed or failed. A completed job carries result; a failed one carries error_message. You can read only jobs you started.
POST /v1/risk-narrative/jobs writes a report’s narrative and saves it as the report’s narrative, as Regenerate does in the app. Send report_type, and optionally scope_key, subject_id and regenerate. Unless you set regenerate, a saved narrative that is still fresh, or one a person edited, comes back completed at once. Its result is the saved narrative: content, title, generated_at and is_user_edited. It needs the Risk module.
POST /v1/finding-suggestions suggests a finding’s title, description or levels (finding_levels_bundle) from an assessment. Send assessment_id, field_name, suggest_context (evals or document_requirement), eval_ids, and optionally the existing_finding_levels already chosen. When every level is already chosen, the job completes at once. An unknown assessment returns 404 with the code assessment_not_found.
POST /v1/vendor-finding-suggestions makes the same suggestion for a vendor finding raised from a Radar alert. Send vendor_id, field_name and radar (alert_id, severity, summary and detector_name). An unknown vendor returns 404 with the code vendor_not_found.
POST /v1/commitment-description-suggestions suggests a commitment description. Send a finding_id, or obligation_ids with an optional vendor_id, and optionally source (finding or obligation). An unknown finding returns 404 with the code finding_not_found.
A suggestion’s result is {"text": ...}: the model’s answer inside <output>, with a <value> and a short <reasoning>, or the chosen level IDs for finding_levels_bundle. Nothing is saved on a finding or commitment; you apply the text yourself.
Score history and AI action decisions
GET /v1/services/{service_id}/risk-score-history, GET /v1/engagements/{engagement_id}/risk-score-history and GET /v1/assessments/{assessment_id}/risk-score-history list how a record’s overall and per-domain scores changed, newest first, with limit and offset, the same way the vendor history does. The assessment history needs the TPRM lifecycle.
POST /v1/agent-actions/{action_id}/decision records what you decided about one Agent Ledger action: decision is accepted, edited or rejected, with an optional note. POST /v1/agent-actions/decisions accepts or rejects many pending actions at once from action_ids; actions no longer waiting on a person come back in skipped with the reason. Deciding on an AI action is a person’s call, so an API key gets 403 with the code api_key_not_allowed. Use a key created by a person whose role holds org_settings:update:org.
POST /v1/commitments/query returns the commitments that match a unified filter, search and sort, one page at a time. A key that reads only assigned commitments sees its own.
Send commitments to a vendor
A vendor answers commitments on its persistent findings portal. These routes put commitments on that portal. None of them emails the vendor: to tell the vendor, invite them to the returned portal.POST /v1/vendors/{vendor_id}/findings-portal opens the vendor’s findings portal without adding anything to it.
POST /v1/commitments/send-to-vendor creates or updates a commitment per finding and puts them all on the vendor’s portal, all or nothing. Send vendor_id and items, each with a finding_id, description and due_at, and a commitment_id to update an existing commitment. A finding of another vendor, or a commitment the vendor already locked, rejects the whole batch with 400.
POST /v1/commitments/send-obligations-to-vendor puts a commitment per obligation in obligation_ids on the vendor’s portal, due at due_at. An obligation’s open commitment is reused.
Edit a commitment and answer the vendor’s round
PATCH /v1/commitments/{commitment_id} edits a commitment’s terms, status or assignee. A field you omit keeps its value, and null clears it. Changing terms the vendor already accepted reopens their round on the portal, with your optional revision_message. A status change notifies the finding’s people. Archiving or unarchiving with is_archived needs a user key; an API key gets 403 with the code api_key_not_allowed.
POST /v1/commitments/{commitment_id}/resolve-round answers the vendor’s latest round with one of the two actions the commitment’s round_actions offers:
A send-back reopens the vendor’s round on the portal with your
message. Nobody is emailed. note is kept as an internal comment. accept, accept_risk, adopt_terms and request_more_work are decisions, so an API key gets 403 with the code api_key_not_allowed. An action the round does not offer returns 400 with the code round_action_not_available.
Commitment and finding documents
GET /v1/findings/{finding_id}/documents lists a finding’s documents: its own, then each of its commitments’. A commitment’s documents carry its commitment_id. GET /v1/commitments/{commitment_id}/documents lists one commitment’s documents.
POST .../documents attaches a file as multipart form data with file, and an optional note. It returns 201 Created. PATCH .../documents/{document_id} replaces the note; null clears it. POST .../documents/{document_id}/archive takes a document off. An uploaded document is archived, while a vendor document stays in the vendor’s library. Archiving needs a user key.
A document never carries its storage location. GET .../documents/{document_id}/download returns a url to download it, valid for an hour, with Cache-Control: no-store.
Create findings in bulk
POST /v1/findings/bulk creates up to 50 findings on one source. Send source_type (assessment, vendor or org_document), source_id and rows. Each row has your own client_row_id and a title, and optionally body, assigned_to_id, vendor_id, due_date, eval_ids, finding_status_id and the level IDs. A row from a document’s finding extraction sends approved_candidate_id, and extraction_source_document_id on an assessment.
Each row lands or fails on its own. The response has the created findings in data, and each failed row’s client_row_id and reason in failed_records. An unknown source returns 400 with the code invalid_finding_source. Assignees and the finding’s people are emailed once each. The vendor is not.