Deal guides
Complete and submit a Deal
Read as MarkdownComplete an existing Deal application: read its questions, save answers, attach documents, check readiness, and submit it to the configured recipient. This works whether the Deal started from a Lead source or a Project match.
Before you start: keep the returned deal_application ID and URLs. Your key needs deal_applications:write with access to this Deal. This permission covers reading, completing, and submitting its application. Creating the Deal or saving answers does not submit it.
Every request uses Authorization: Bearer <api-key>. JSON writes use Content-Type: application/json. Give each answer write, file upload, and submission its own Idempotency-Key; retain the exact request and key for retries. Examples below show abbreviated responses and illustrative IDs. Use returned URLs and the live contract's field keys in your integration.
1. Read the application and its questions
Start with the application's url returned when the Deal was created:
GET /v1/deal-applications/dapp_example/
Response excerpt · 200:
{
"id": "dapp_example",
"status": "is_pre_submitted",
"answers_revision": 0,
"submitted_at": null,
"contract_url": "https://YOUR_API_HOST/v1/deal-applications/dapp_example/contract/",
"answers_url": "https://YOUR_API_HOST/v1/deal-applications/dapp_example/answers/",
"submit_url": "https://YOUR_API_HOST/v1/deal-applications/dapp_example/submit/",
"submission_readiness_url": "https://YOUR_API_HOST/v1/deal-applications/dapp_example/submission-readiness/"
}
Read contract_url for the application’s questions and requirements. Its questions define the accepted values and supported operations; different recipients or Deal packages can use different fields.
Example contract excerpt — this example assumes a form with a requested amount and a financial-statements upload.
{
"questions": [
{
"block_key": "request",
"label": "Funding request",
"answer_operations": ["read", "update"],
"fields": [
{
"field_key": "amount",
"label": "Requested amount",
"value_schema": { "type": "string" }
}
]
},
{
"block_key": "financial_statements",
"label": "Financial statements",
"answer_shape": "file",
"answer_operations": ["read", "upload", "delete"]
}
]
}
Read the contract's instructions, visibility rules, required fields, and allowed values before mapping your data. The contract also returns operation URLs for answers, repeated items, files, and submission.
2. Save or change an answer
GET answers_url first. Retain its ETag exactly, including quotes. This prevents your update from overwriting changes made by the client, another integration, or a Portal user.
Request — for an answer response with ETag: "0":
PATCH /v1/deal-applications/dapp_example/answers/
If-Match: "0"
Idempotency-Key: deal-example-amount-1
{
"updates": [
{
"block_key": "request",
"field_key": "amount",
"value": "250000"
}
]
}
Response excerpt · 200, with ETag: "1":
{
"object": "deal_application_answers",
"answers_revision": 1,
"updated_answers": [
{ "block_key": "request", "field_key": "amount", "value": "250000" }
]
}
To correct the amount later, send the same field keys with the new value, a new idempotency key, and the latest answers ETag. The response’s updated_answers contains the saved values, and its ETag is ready for your next answer write. Read answers again when you need the complete application or to resolve a conflict; follow next_cursor when the list spans several pages.
For a repeated group such as owners, POST { "block_key": "owners" } to the contract's repeatable_items_url using the latest answers ETag and a new idempotency key. Save the returned item.item_id, then PATCH that URL with item_id and updates. Use group and field keys from your contract. See repeated-item operations for the full schemas.
3. Attach a document
Files are uploaded as bytes, not placed in the answer JSON. Request an upload URL from the contract's file_uploads_url:
POST /v1/deal-applications/dapp_example/answers/files/
{
"block_key": "financial_statements"
}
For a document inside a repeated group, also include that group's returned item_id.
Response excerpt · 201:
{
"object": "file_upload",
"upload_url": "https://YOUR_API_HOST/v1/deal-applications/dapp_example/answers/files/upl_example/",
"multipart_field": "file"
}
The full response includes expires_at and max_size_bytes. Send one multipart field named file to the returned URL before it expires. For example:
curl --request POST "$UPLOAD_URL" \
--header "Authorization: Bearer $LEVR_API_KEY" \
--header 'If-Match: "1"' \
--header 'Idempotency-Key: deal-example-statements-1' \
--form 'file=@financial-statements.pdf'
Use the latest answers ETag, replacing "1" above. Let your HTTP client set the multipart content type and boundary.
Response excerpt · 201, with a new answers ETag:
{
"object": "answer_file_upload",
"answers_revision": 2,
"file": {
"id": "ansfile_example",
"block_key": "financial_statements",
"filename": "financial-statements.pdf"
}
}
The response confirms the attachment. GET the contract’s files_url when you need file metadata or a fresh download URL. After a connection failure, retry the original upload URL, file, key, and ETag to recover the result. Request a new upload URL only after a confirmed expiry rejection. On 412, read the latest answers and reconcile changes before creating a new write attempt.
4. Complete and submit the application
Choose how to finish: email the client a link to complete the form, or submit the completed application through your integration.
Let the client complete the application
If the client will complete and submit the form, email the client a secure link to a branded web form where they can fill in answers, upload documents, and submit the application when it is ready. Any information you have already saved appears in the form, so they can continue where your integration left off.
Set access_mode to edit for each recipient who should complete the form. Omit it or use view_only for someone who only needs to read it. The recipient gets the link in their inbox. Submission is available when the application supports it and has not already been submitted.
Your API key needs deal_application_sharing:write. For example:
POST /v1/deal-applications/dapp_example/share/
Idempotency-Key: deal-example-share-1
{
"recipients": [{ "email": "maya@example.com", "access_mode": "edit" }],
"message": "Please complete and submit your application."
}
Response excerpt · 201:
{
"grants": [
{ "email": "maya@example.com", "access_mode": "edit" }
],
"delivery_state": "queued"
}
The request emails the link to each recipient. delivery_state: "queued" means the email is waiting to be sent, not that the client has received or reviewed it.
Retry the same request with its original idempotency key if the response is lost. Sharing again with a new key refreshes the recipient's invitation and revokes their previous sessions; use that when you intend to resend access.
Submit through your integration
With deal_applications:write, GET the returned submission_readiness_url to check that the completed application is ready:
GET /v1/deal-applications/dapp_example/submission-readiness/
Ready response excerpt · 200:
{
"has_blocking_issues": false,
"blocking_issue_count": 0,
"issues": [],
"submission_token": "<returned-submission-token>"
}
Readiness checks required answers, documents, application status, and delivery setup. Correct the issues it identifies, or ask the Portal administrator to resolve delivery configuration, then check again. A ready response includes a submission_token for the checked application and answers. Pass it unchanged when submitting:
POST /v1/deal-applications/dapp_example/submit/
Idempotency-Key: deal-example-submit-1
{
"submission_token": "<returned-submission-token>"
}
Response excerpt · 200:
{
"id": "dapp_example",
"status": "is_submitted_to_processor",
"answers_revision": 2,
"submitted_at": "2026-10-15T18:00:00Z"
}
A direct application to your own Portal can use the published Portal delivery method. Submission updates the existing Deal and runs the normal submission notifications and events; it does not create another Deal. You do not need an additional email or connector delivery method for this flow.
Verify submitted_at and the returned status. Submission runs the Deal's configured delivery workflow; it does not mean the recipient has approved the application. If the application or its answers changed after the check, submission returns 412. Review the changes, check readiness again, and submit the new token with a new idempotency key. After a connection failure, retry the same token and original key to recover the result.
5. Follow application updates
Before submission, ask a Portal administrator to add your HTTPS receiver in API settings → Webhooks and select:
| Event | What to do |
|---|---|
deal_application.updated | Reread answers when someone changes the application. |
deal_application.submitted | Record submission and read the current application. |
deal_application.status_changed | Refresh the application status in your system. |
deal.stage_changed | Refresh the Deal's pipeline stage, if you also track processing progress. |
Reading the Deal’s pipeline stage also requires deals:read.
Follow the webhook receiver guide for signature verification, deduplication, retries, and out-of-order delivery. Treat each event as a prompt to read the current resource. You can also poll the application detail URL for status and submitted_at; respect rate limits.
API integrations follow updates through webhooks or REST polling. The Portal and shared form manage their own live websocket connections; your integration does not need to connect to those. A live form update does not mean the application has been submitted.
Where the same application appears in the Portal
In a Portal with access to the Deal, open Deals, find the business's Deal, and open its application. An authorized client opens that same application's shared form. Both surfaces use the same saved answers and documents. Changes made by someone with editing access appear in that application.
Access and actions still depend on the caller. A client invitation, a Portal user, and an API key have different permissions. Submitting through the API sends the application on the business’s behalf. Approving or declining it is a separate action for the receiving team.
