Documents
A document is a file on a deal: a pay stub, a statement, an appraisal, a signed disclosure. Every document belongs to a deal, so you find it through the deal. This page covers listing, uploading, filing, and downloading documents, and the list of what a client still owes.
This API is in beta. Any endpoint can change in a breaking way without notice, so watch the
changelog. A key with the no_documents limit can't reach any of this; see
Authentication.
List a deal's documents
Code
Code
Each row describes the file. It doesn't contain the file itself; see Download a file.
| Field | What it means |
|---|---|
file_name, file_type, file_size | The file as it was uploaded. |
display_name | The name brokers see in BrokerPlus. |
document_type, document_type_label | What BrokerPlus classified it as, such as a pay stub or a T4. |
classification_status | Whether that classification is final or still running. |
stage, verified | Whether someone checked the file and it is ready for a lender. |
folder | Where a verified file is filed, such as income. Null until verified. |
uploaded_via | Where it came from: a broker, this API, an email, or the client. |
uploaded_by_user_id, uploaded_by_name | The broker who added it. Null when a client or an email did. |
version | The revision count. A newer document can supersede an older one. |
excluded_from_submission | True when the broker marked the file to stay out of lender submissions. |
Filter with verified, document_type (a code from GET /v1/document-types), contact_id for one
borrower's files, or updated_since for an incremental sync (see Sync).
Upload a document
POST /v1/deals/{id}/documents adds a file to a deal. BrokerPlus stores, classifies, and checks it
exactly as it does a file a broker drops in the app, and fires the document.uploaded
webhook.
Send the file as multipart/form-data in a field named file:
Code
Code
The response is shortened here. Everything besides the file is optional:
| Field | What it does |
|---|---|
document_type | The type you expect, as a code from GET /v1/document-types. BrokerPlus checks it. |
contact_id | The borrower the file belongs to. It must be an applicant on the deal, not a partner. |
name | The name brokers see. Without it, BrokerPlus names the file from its type. |
If your client can't send a form, send JSON with the file in base64:
Code
The limits match the app:
- A form upload can be up to 50 MB, and a base64 upload up to 10 MB before encoding. Larger files get
413 FILE_TOO_LARGE. - The file must be a PDF, JPEG, PNG, TIFF, WebP, Word (
.docx), Excel (.xlsx), or CSV, and its content must match its type. Otherwise you get400 VALIDATION_ERROR. - A PDF must open without a password:
422 LOCKED_PDFfor a locked one,422 INVALID_PDFfor one that can't be read.
A photo is stored as a one-page PDF, so file_type can differ from what you sent.
Send an Idempotency-Key with every upload. Without one, a retry after a dropped connection adds
a second copy. See Idempotency and batch.
An upload can't choose a folder or set verified. A document becomes verified when a broker checks
it, or when BrokerPlus checks it and the brokerage lets it verify documents. To verify one yourself,
move it after the upload.
Change a document
PATCH /v1/documents/{id} renames a document, files it under another borrower, or keeps it out of
the next lender submission. Send only what changes.
Code
Code
The contact_id must be an applicant on the deal; a partner such as a realtor gets 400. Send
"display_name": null to let BrokerPlus name the file from its type again.
POST /v1/documents/{id}/stage moves a document to another tray on the deal's document board, the
same as a drag in BrokerPlus. {"stage": "verified", "folder": "income"} marks it checked and files
it; verified with another folder moves it between folders; pending or review takes the check
back. Nothing goes to the client.
DELETE /v1/documents/{id} deletes the file from the deal and from storage, for good.
There's no reject. In BrokerPlus, a rejection asks the client for a new copy, which emails them. To ask for a document, use a document request (below).
Notes on a document
Brokers write notes on a document in its preview in BrokerPlus, for example "Page 2 is missing." Only the broker's team sees them. A client never does.
GET /v1/documents/{id}/notes lists them, newest first. POST /v1/documents/{id}/notes adds one
with a body. Send parent_id to reply to a note. A reply can't have replies of its own. A teammate
mentioned in the body is told, the same as in BrokerPlus.
PATCH /v1/documents/{id}/notes/{noteId} changes a note's body, and
DELETE /v1/documents/{id}/notes/{noteId} deletes it with its replies. Only the broker who wrote a
note can change or delete it: for anybody else the note is 404 NOT_FOUND.
Documents a client still owes
GET /v1/deals/{id}/document-requests lists each document BrokerPlus asked a client for and hasn't
received yet, one row per applicant who owes it.
Code
Code
A row leaves the list when the client sends a file for it or a broker dismisses it. rejected is
true when the client's file was refused and the request is open again.
To ask the client for more documents, POST /v1/deals/{id}/document-requests does what the Request
Documents window does. It emails the client now, so a key with the no_sending limit can't use it.
See Ask a client for something.
The team's own document types
GET /v1/document-types lists the documents BrokerPlus knows. A team can add its own for anything
else it asks clients for, such as a gift letter in a particular form. POST /v1/custom-document-types
adds one with a label and, if you want, a category and a description that the client sees.
BrokerPlus makes a code from the label, for example custom:gift_letter_from_parents_4d4c4a2e.
Use that code in a document package.
Two types in a team can't have the same label: the second gets 409 DUPLICATE_LABEL.
DELETE /v1/custom-document-types/{id} takes a type out of the library. A request that a client
already has keeps asking for it under its label.
Document packages
A document package is a named set of documents that the broker's team asks for together, for
example Self-Employed Ontario: the notice of assessment, two years of T1 generals, and business
bank statements. Packages belong to the broker's team. A key reaches the packages of the team its
broker is on, and no other team's.
POST /v1/doc-packages makes one. document_types takes codes from GET /v1/document-types.
notes holds one short line for the client under each document.
Code
Code
Two packages in a team can't have the same name: the second gets 409 DUPLICATE_NAME.
PATCH /v1/doc-packages/{id} changes only the fields you send, and DELETE /v1/doc-packages/{id}
deletes a package. A request that the client already has keeps its documents, because a request
copies a package and does not link to it.
condition_key names a situation that BrokerPlus can recognize on a submitted application. When an
application matches it, BrokerPlus ticks the package on the deal's next request. It never sends the
package by itself. GET /v1/doc-packages/samples lists the situations, each with the package it
starts as, and POST /v1/doc-packages/samples/{key} adds one to the team.
A package sends nothing. To ask a client for its documents, send its document_types to
POST /v1/deals/{id}/document-requests, which asks the client now.
PDFs in the client hub
The Resources tab of a client's hub holds PDFs from the broker, such as a guide for first-time buyers or a closing checklist. A brokerage PDF goes to every client. A deal's own PDF goes to that deal's client alone.
POST /v1/hub-resources adds one. Send deal_id for a deal's own PDF, or leave it out for a
brokerage PDF. Send the file as multipart/form-data, or as JSON:
Code
Code
Only a PDF of at most 20 MB is accepted, and each list holds at most 12. The client sees the PDF the
next time they open their hub. BrokerPlus does not tell them; to send a note about it, use
BrokerPlus. Because the client sees it, a key with the no_sending limit can't add a PDF
(403 KEY_LIMITED), but it can remove one. GET /v1/hub-resources lists the brokerage PDFs, and ?deal_id= lists one deal's.
DELETE /v1/hub-resources/{id} removes a PDF from the hub and from storage.
Files that came in by email
When a client emails a file and BrokerPlus matches the email to a deal, the file waits for the
broker before it goes on the deal file. GET /v1/deals/{id}/email-attachments lists the files that
wait, newest email first. Send status=accepted, dismissed or all to see the files the broker
already decided.
POST /v1/deals/{id}/email-attachments/{attachmentId}/acceptputs the file on the deal file as a new document.document_idin the response is that document, and BrokerPlus classifies it as it does an upload.POST /v1/deals/{id}/email-attachments/{attachmentId}/dismisskeeps the file off the deal file. The email stays in the inbox.
Code
A file is decided once. A second accept returns the same document, and a dismiss after an accept
gets 409 ALREADY_RESOLVED. Read the file itself through its document with
GET /v1/documents/{id}.
Generated documents and signing
Two more kinds of document live on a deal, and both are covered in Ask a client for something:
- Generated documents, such as an approval letter, made from a template with
POST /v1/deals/{id}/generated-documentsand filed as a PDF on the deal. - Documents to sign, added with
POST /v1/deals/{id}/signing-documentsand sent in the broker's name.
Funding packages
A funding package holds the lender's forms that the broker gets ready after the commitment: a request for funds, a PAD form, an acknowledgement. For each document, BrokerPlus reads the form fields, decides who fills each one, and writes in the values it holds on the deal. You can do every step through the API, including sending it for signature.
- Add the forms. Send documents already on the deal to
POST /v1/deals/{id}/funding-package/itemswithdocument_ids, or upload new ones toPOST /v1/deals/{id}/funding-package/upload. The first document starts the package. - Map each form with
POST /v1/deals/{id}/funding-package/items/{item_id}/map. Each field gets a label, aparty, and avaluefromGET /v1/deals/{id}/funding-package/context. Values are never made up: a field with no match stays empty. - Check the item with
PATCH /v1/deals/{id}/funding-package/items/{item_id}. Set the person who signs it inrecipient_contact_id, correctfieldsif you need to, and sendstate: reviewed. Only a reviewed item can be sent.
Code
GET /v1/deals/{id}/funding-package returns the open package, or 404 when the deal has none.
Adding a document that is already in the package adds nothing, so you can retry an add.
DELETE /v1/deals/{id}/funding-package/items/{item_id} takes a document out of the package and
leaves it on the deal. A key with the no_documents limit can't reach a funding package.
Send it for signature
POST /v1/deals/{id}/funding-package/send sends the package now, in the name of the broker your key
acts as. Every document must be reviewed, with a person who signs it who has an email, or you get
422 with each problem in details.fields. A key with the no_sending limit can't send a package
(403 KEY_LIMITED), and if that broker may not contact clients you get 403 FORBIDDEN.
BrokerPlus fills each form and puts them all in one envelope. Each person gets one email from the
signing service, however many forms are theirs. The response is 201 with the envelope. If the
signing service refuses the package, you get 409 NOT_SENT, and nothing is sent.
GET /v1/deals/{id}/signing-envelopes lists the envelopes that went out on the deal, with the file
names, the people each went to, and status: sent, then signed once everyone has signed, or
declined or voided. You can't void an envelope or send a reminder through the API.
Download URLs
A list never contains a download URL. Ask for the single document to get one:
Code
Code
A download URL carries its own authorization and expires after five minutes. Anyone holding an unexpired URL can fetch the file without a key, so treat it like the file itself:
- Get the URL, then follow it right away. Don't store it, cache it, or queue it.
- Keep it out of browsers you don't control and out of anything that writes logs.
- Ask again when you need it. Each request gives a fresh URL at no cost; an old one can't be extended.
download_url_expires_at is the exact moment the URL stops working. When you process many
documents, fetch each URL just before you download that file. Fetch them all up front and the last
ones expire before you get to them.