# Suggestions, notifications, and deal checks

These endpoints show you what the broker sees on their own screens: the suggestions waiting for
them, the notifications in their bell, a deal's compliance checklist and credit reports, and the
activity trail of a deal or a contact.

This API is in beta. Any endpoint can change in a breaking way without notice, so watch the
[changelog](/changelog).

## Suggestions

BrokerPlus suggests changes to the broker: a new contact from an email, a follow-up task, a document
to file, a stage move. `GET /v1/suggestions` lists them; add `status=pending` for the ones still
waiting on a decision.

<CodeTabs syncKey="lang">

```bash title="curl"
curl "https://api.mos.getbrokerplus.ca/v1/suggestions?status=pending" \
  -H "Authorization: Bearer $BROKERPLUS_API_KEY"
```

```ts title="TypeScript"
const res = await fetch("https://api.mos.getbrokerplus.ca/v1/suggestions?status=pending", {
  headers: { Authorization: `Bearer ${process.env.BROKERPLUS_API_KEY}` },
});
const { data } = await res.json();
```

</CodeTabs>

```json title="Response"
{
  "success": true,
  "data": [
    {
      "object": "suggestion",
      "id": "cmsg8f2k1",
      "kind": "task_create",
      "title": "Call Sam about the appraisal",
      "status": "pending",
      "values": { "title": "Call Sam about the appraisal", "due_date_iso": "2026-10-09" },
      "approvable": true,
      "deal_id": "cmsd71x0q",
      "deal_name": "Sam Lee - 12 Elm Street",
      "subject_type": "communication",
      "subject_id": "cmsc0p4n7",
      "bundle_key": null,
      "bundle_label": null,
      "task_id": null,
      "created_by_agent": true,
      "expires_at": null,
      "dismissed_at": null,
      "dismiss_reason": null,
      "executed_at": null,
      "created_at": "2026-10-07T14:02:11.000Z",
      "updated_at": "2026-10-07T14:02:11.000Z"
    }
  ],
  "pagination": { "total": 1, "page": 1, "page_size": 25, "total_pages": 1 }
}
```

`POST /v1/suggestions/{id}/approve` carries out a suggestion as the key's broker.
`POST /v1/suggestions/{id}/dismiss` turns it down; send a `reason` and the broker sees it.

A key can approve a suggestion only when `approvable` is `true`. Anything that reaches a client, acts
outside BrokerPlus, or opens a draft stays with the broker: approving it returns
`409 APPROVE_IN_BROKERPLUS`, and the suggestion stays in their queue. When a task asks the broker to
approve it, `task_id` names that task. It is the same decision, not a second one.

A suggestion is BrokerPlus asking the broker. A request from your key is the broker acting, so it
never becomes a suggestion. See [Reaching clients](/reaching-clients).

## Notifications

`GET /v1/notifications` lists the notifications in the bell of the key's broker, never a teammate's.
Add `unread_only=true` for the unread ones. `POST /v1/notifications/{id}/read` marks one read, and
`POST /v1/notifications/read-all` marks them all and says how many:

```json title="Response to POST /v1/notifications/read-all"
{ "success": true, "data": { "updated": 3 } }
```

## Compliance checklist

`GET /v1/deals/{id}/compliance` returns the deal's compliance checklist as the broker sees it: the
checkpoints and open items, and for each party the identity verification, know-your-client record,
anti-money-laundering screening, risk rating, and politically exposed person question. It also has
the consents, the suitability answers, and the required documents.

```json title="Response (shortened)"
{
  "success": true,
  "data": {
    "object": "deal_compliance",
    "deal_id": "cmsd71x0q",
    "checkpoints": [
      { "key": "intake_credit", "label": "Intake + credit", "status": "ready", "blocker_count": 0, "pending_reason": null }
    ],
    "parties": [
      {
        "contact_id": "cmsc8a1v2",
        "name": "Sam Lee",
        "role": "primary_applicant",
        "is_entity": false,
        "identity": { "status": "verified", "completed_at": "2026-10-02T16:20:00.000Z", "expires_at": null, "method": "government_photo_id" },
        "kyc": { "status": "cleared", "completed_at": "2026-10-02T16:25:00.000Z", "expires_at": null, "has_record": true }
      }
    ],
    "consents": [
      { "contact_id": "cmsc8a1v2", "scope": "credit_pull", "status": "active", "consent_id": "cmsn2k7q0", "granted_at": "2026-10-01T15:00:00.000Z", "expires_at": "2026-10-31T15:00:00.000Z" }
    ],
    "legal_hold": false
  }
}
```

The checklist is read-only. Overriding a check or marking an item done happens in BrokerPlus. A
`consent_id` points at a consent you can read with `GET /v1/deals/{id}/consents`.

The response leaves out the ID document, its images and number, the date of birth, the screening
results, and the risk score. Each status tells you whether the broker has work to do; the details
stay in BrokerPlus.

### Evidence for a custom requirement

A requirement that the brokerage added to the deal has a `custom_obligation_id` in
`document_requirements`, and `evidence` lists the documents that answered it.
`POST /v1/deals/{id}/compliance/custom-obligations/{obligationId}/documents` uploads a file and
attaches it to the requirement, as the requirement's upload button does. Send the file as
`multipart/form-data` in a `file` field, or as JSON with `file_name`, `content_type`, and
`content_base64`. The file is also a document on the deal. `DELETE` on
`.../documents/{documentId}` takes the document off the requirement and leaves it on the deal.

## Know-your-client records

A know-your-client record is the FINTRAC information record of one party on a deal, as the KYC
editor on the deal's compliance tab shows it. Read the records of a deal with
`GET /v1/deals/{id}/kyc-records`, and one party's record with
`GET /v1/deals/{id}/kyc-records/{contactId}`.

`PATCH /v1/deals/{id}/kyc-records/{contactId}` saves a party's record. It makes the record if the
party has none. Only the fields that you send change. `third_parties` replaces the whole list.

```json title="Request"
{
  "relationship_purpose": "purchase_financing",
  "occupation": "Nurse",
  "third_party_determination": "no_third_party",
  "third_party_measures": "Asked the client at the meeting."
}
```

A deal imported from another system can carry the identity check and the politically exposed person
answer from that system, in the fields that start with `id_` and `pep_`. These fields are read only.
`id_document_number` shows only the last four characters, and the credit file number is not
published.

For a company on the deal, `GET` and `PATCH /v1/deals/{id}/entity-verifications/{contactId}` read
and save the entity verification: whether the broker confirmed that the entity exists, who owns and
controls it in `controllers`, and who can sign for it. A person gets `422 NOT_ENTITY`. The
documents that the broker attached in BrokerPlus are not published.

These routes reach a deal that the broker can change. A deal on another team, or a contact who is not
on the deal, gets `404 NOT_FOUND`.

## Identity verifications

`GET /v1/deals/{id}/identity-verifications` lists the identity verifications of the people on the
deal, newest first: the method, the status, and the evidence that each one examined. A verification
belongs to the person, so a verification started from another deal is in the list too.
`document_number` shows only the last four characters. The images of the document, the reports, and
the credit file reference are not published. The list is read-only: the broker reviews a
verification in BrokerPlus.

## Deal risk assessment

The risk assessment rates the transaction, not the client. `GET /v1/deals/{id}/risk-assessment`
reads it, as the broker fills it in on the deal's compliance tab: the reportable transaction types,
the fraud and money-laundering questions, the FINTRAC report, and the broker's risk level.

`PATCH /v1/deals/{id}/risk-assessment` saves it, and makes it if the deal has none. Each answer is
`yes` or `no`. The answers you send are added to the answers on file, so you can save one section at
a time. `"completed": true` marks the assessment complete.

```json title="Request"
{
  "transaction_flags": { "large_cash_transaction": "no" },
  "fraud_answers": { "criminal_history": "no", "unjustified_cash": "no" },
  "risk_level": "low",
  "method_notes": "Long-time client, salaried income, no cash in the down payment."
}
```

An unknown question key gets `400 VALIDATION_ERROR`. The politically exposed person answer is read
only. `identity_verified` is the broker's statement for the deal and does not clear a party's
identity check.

## Credit reports

`GET /v1/deals/{id}/credit-reports` lists the deal's credit reports: the score, the bureau, when
BrokerPlus got it, and the account totals the deal shows.

```json title="Response (shortened)"
{
  "success": true,
  "data": [
    {
      "object": "credit_report",
      "id": "cmscr0d81",
      "contact_id": "cmsc8a1v2",
      "contact_name": "Sam Lee",
      "bureau": "Equifax",
      "status": "complete",
      "score": 742,
      "report_date": "2026-09-30",
      "pulled_at": "2026-09-30T18:11:04.000Z",
      "consent_id": "cmsn2k7q0",
      "detail_pending": false,
      "tradeline_summary": { "total_trades": 3, "open_trades": 2, "total_balance": 18250, "utilization": 42.5 }
    }
  ]
}
```

The report itself, the accounts, and the account numbers stay out. The API can't pull credit: a pull
costs money and needs the client's consent, so the broker does it in BrokerPlus.

## Activity trail

`GET /v1/deals/{id}/activity` returns a deal's trail, newest first: messages and calls, broker notes,
and changes to the deal. `GET /v1/contacts/{id}/activity` returns the messages and calls with one
person, plus changes to the deals they are on.

Each entry has a `summary`, and a message or note also has a `preview`. Follow `record_type` and
`record_id` to the full record; a `communication` entry, for example, reads with
`GET /v1/communications/{id}`.

```json title="Response (shortened)"
{
  "success": true,
  "data": [
    {
      "object": "activity",
      "id": "cmsev41p2",
      "kind": "event",
      "occurred_at": "2026-10-07T13:40:00.000Z",
      "summary": "Stage changed",
      "preview": null,
      "type": "stage.transitioned",
      "actor_name": "Alex Broker",
      "deal_id": "cmsd71x0q",
      "record_type": "deal",
      "record_id": "cmsd71x0q"
    }
  ]
}
```

The trail holds the newest 200 entries of each kind. Mail from a mailbox the broker can't read isn't
on it.

## Who a deal is waiting on

A deal card shows who owes the next move: the client with documents, or a lender with an answer.
BrokerPlus keeps each of these as a watch. It starts one when the deal begins to wait, raises its
`escalation_level` as the wait gets longer, and ends it when the answer arrives.
`GET /v1/work-watches` lists them, and `GET /v1/work-watches/{id}` reads one.

```bash
curl "https://api.mos.getbrokerplus.ca/v1/work-watches?deal_id=cmsd71x0q&status=active" \
  -H "Authorization: Bearer $BROKERPLUS_API_KEY"
```

```json title="Response (shortened)"
{
  "success": true,
  "data": [
    {
      "object": "work_watch",
      "id": "cmsw81k2a",
      "deal_id": "cmsd71x0q",
      "kind": "waiting_on_borrower",
      "status": "active",
      "waiting_on": "borrower",
      "waiting_on_contact_id": "cmsc11b7e",
      "waiting_on_user_id": null,
      "since_at": "2026-10-02T14:00:00.000Z",
      "due_at": null,
      "escalation_level": 1,
      "task_id": "cmst90q4d"
    }
  ]
}
```

Filter by `deal_id`, `status`, and `waiting_on`. Use `updated_since` to sync the changes. Watches
are read-only: to change what a deal waits for, change the deal, its documents, or its conditions.

## Goals

The goals card on the Business pages holds the broker's targets: funded volume, funded deals, or
commission over a period, for the whole book or for one lender. `GET /v1/goals` lists them with
`pace`, which says where the broker stands today. BrokerPlus works `pace` out from the funded deals
each time you read a goal, so it moves without the goal changing.

```bash
curl https://api.mos.getbrokerplus.ca/v1/goals \
  -H "Authorization: Bearer $BROKERPLUS_API_KEY"
```

```json title="Response (shortened)"
{
  "success": true,
  "data": [
    {
      "object": "goal",
      "id": "cmsg71q0a",
      "metric": "funded_volume",
      "broker_id": "cmsu12k9d",
      "lender_id": null,
      "period_start": "2026-01-01",
      "period_end": "2026-12-31",
      "target": 5000000,
      "curve": "seasonal",
      "pace": { "actual": 3100000, "expected": 3400000, "gap": -300000, "status": "on_track", "progress": 0.62 },
      "gap_in_deals": 0.6,
      "within_reach": false
    }
  ]
}
```

`POST /v1/goals` sets a goal for the key's broker. A goal is named by its `metric`, `period_start`
and `lender_id`, so setting the same three again replaces the target. `PATCH /v1/goals/{id}`
changes the target, the end of the period, the curve, or the months the broker set by hand.
`DELETE /v1/goals/{id}` deletes one. A key reads the goals of the broker whose reports it can read,
and it changes only its own broker's goals.

## In an AI assistant

The [MCP server](/mcp) has a tool for every operation on this page: suggestions, notifications, the
activity trail, who a deal is waiting on, and goals. It also has tools for the compliance checklist,
know-your-client records, identity verifications, the risk assessment, and credit reports. An
assistant can repeat what it reads to anyone it talks to, so treat a key you give an assistant the
way you treat the client's file.
