Skip to content
Levr API Documentation
Download OpenAPI specOpenAPI

Hand off to an AI agent

Copy instructions to your AI agent

Copy this guide into your AI agent. It links to the current REST API and MCP documentation.

You are helping someone integrate their system with Levr. Use Levr's published documentation and machine-readable contracts as the source of truth. Do not invent endpoints, fields, or MCP tools.

  1. Start with the Levr API documentation to choose between a server-to-server REST integration and a user-authorized MCP connection.
  2. For REST, read the Markdown REST API guide, choose the user’s task, then fetch only the relevant resource guide and operation Markdown. Resource indexes include task guidance. For funding recommendations, start with the Matchmaking guide. Use the OpenAPI specification for exact paths, authentication, parameters, and envelope schemas.
  3. For REST client intake, Deal applications, or project Criteria, fetch that resource's contract_url. Its Application Contract is the authority for the current questions, stable keys, repeatable groups, file-transfer steps, accepted answer shapes, conditional visibility, supported operation URLs, and integration instructions. Repeatable fields require the returned item_id. File bytes use the expiring upload_url and multipart field named file; they never belong in JSON. Do not substitute a static example or the latest application definition.
  4. For an AI client acting with a signed-in user's access, read the MCP connection instructions in Markdown first, then the Model Context Protocol guide if needed. Let the connected MCP server expose its current tools and input contracts; do not infer tools from the REST API. Read the relevant MCP resource guide and follow the discovered tool’s revision, file-transfer, and confirmation requirements.
  5. Use URLs from the record handoff when supplied; otherwise use this documentation environment. Keep API keys and file-transfer credentials out of browser code, logs, prompts, and source control.
  6. Before implementing a write, verify authentication, required inputs, idempotency, error responses, and retry guidance in the current contract. If an operation is not documented, do not assume it exists.

Early access

Request an integration

Tell us what your integration needs to do. Add an email if you want a reply.

Deal guides

Complete and submit a Deal

Read as Markdown

Complete 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:

EventWhat to do
deal_application.updatedReread answers when someone changes the application.
deal_application.submittedRecord submission and read the current application.
deal_application.status_changedRefresh the application status in your system.
deal.stage_changedRefresh 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.