Docs/API/Document Upload

Document Upload

Upload financial documents attached to a partner application. PGI's extraction pipeline runs automatically after upload and fires a document.extracted webhook when complete.

Endpoint

POST /api/v2/applications/{application_id}/documents/

Uploads a file and links it to the specified application. The upload is tenancy-gated: your API key must have an active PartnerApplication link to the target application. Attempting to upload to an application that belongs to another partner returns 403.

Authentication

Bearer token only. Use your pk_live_* or pk_test_* key. Session auth and JWT are not accepted on this endpoint.

HTTP Header
Authorization: Bearer pk_live_abc123def456ghi789jkl012mno345

Request fields

Send the request as multipart/form-data.

FieldTypeDescription
filerequired file The document to upload. Maximum 5 MB. Allowed types listed below.
documentTyperequired string One of the canonical document type values listed below. Defaults to other if omitted or unrecognized.

File constraints

Maximum size

5 MB per file. Requests with a larger file body return 400 before the file is processed.

Allowed MIME types

The partner upload endpoint does not accept images or binary formats. Only structured document types are permitted:

MIME typeExtension
application/pdf.pdf
application/vnd.openxmlformats-officedocument.wordprocessingml.document.docx
application/vnd.ms-excel.xls
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.xlsx
text/csv or application/csv.csv
text/markdown or text/x-markdown.md
Note on MIME detection Some HTTP clients send application/octet-stream as the content type regardless of the actual file format. PGI falls back to file extension detection in this case, so a file named financials.xlsx will be accepted even if the client sends an octet-stream header.

Document types

Use the documentType field to tell PGI's extraction pipeline what kind of document you're uploading. Correct typing improves extraction accuracy significantly.

ValueFull labelUse caseRequired for CA
loan_agreement Lender Agreement / Term Sheet Loan agreement or term sheet from the lender. Required for all Canadian Purbeck submissions to validate loan terms and structure. Yes
profit_loss Profit & Loss Statement (12 months, monthly) Monthly P&L showing revenue, COGS, EBITDA, and net income. Primary document for income verification. Yes
balance_sheet Balance Sheet (most recent month-end) Assets, liabilities, and equity at the most recent month-end. Required for DSCR and leverage analysis. Yes
ar_aging Accounts Receivable Aging Summary Outstanding receivables by aging bucket. Used to assess cash conversion and concentration risk. Yes
ap_aging Accounts Payable Aging Summary Outstanding payables by aging bucket. Used to assess working capital and payment history. Yes
founder_cv Founder CV (startup, under 3 years) Resume or bio for the principal guarantor. Required only for startup underwriting when the business is under 3 years old. If startup
financial_forecast Financial Forecast (startup, under 3 years) 12-24 month revenue and expense projections. Required only for startup underwriting when the business is under 3 years old. If startup
Canadian submissions For Canadian applications being sent to Purbeck, five core financial documents are always required: loan_agreement, profit_loss, balance_sheet, ar_aging, and ap_aging. Additionally, founder_cv and financial_forecast are required only if the business formation date (q26_formation_date) indicates the business is under 3 years old. All documents are uploaded via the documents endpoint after the application is created and are validated at the collect/preflight step before submission to the underwriter. All documents uploaded via the partner API are automatically included in the Purbeck submission by default (include_in_submission=True). Contact PGI if you need to exclude a specific document.

Response (201 Created)

JSON Response -- 201 Created
{
  "documentId": "48CE8D0CCC804D72A269C452A5",
  "extractionJobId": "48CE8D0CCC804D72A269C452A5",
  "applicationId": "F790E7CE7ADB46BBBD56A7CDAA"
}
FieldTypeDescription
documentId string Unique identifier for the uploaded document.
extractionJobId string Job identifier for the async extraction pipeline. Currently equals documentId. Use this to correlate with the document.extracted webhook.
applicationId string The application the document was linked to.

Error responses

StatusWhenBody
400 File is missing from the request {"error": "No file provided. Include a 'file' field in the multipart request."}
400 File exceeds 5 MB {"error": "File size exceeds the 5 MB limit for partner document uploads."}
400 Unsupported MIME type (e.g. image/jpeg) {"error": "File type 'image/jpeg' is not allowed. Accepted types: PDF, DOCX, XLS, XLSX, CSV, Markdown."}
401 Missing or invalid API key {"detail": "Invalid API key."}
403 Application does not belong to your partner account {"error": "Application not found or not accessible with this API key."}
429 Rate limit exceeded {"detail": "Request was throttled."}

Sandbox isolation

When you upload with a pk_test_* key, the resulting Document record is flagged is_sandbox=True. Sandbox documents are hidden from PGI's production admin views and do not trigger real underwriter workflows. The extraction pipeline runs in full, giving you realistic extracted data to test against.

document.extracted webhook

After the extraction pipeline completes, PGI fires a document.extracted webhook to your registered webhook_url. This happens asynchronously, typically within 30 seconds of upload.

document.extracted Payload
{
  "event": "document.extracted",
  "document_id": "48CE8D0CCC804D72A269C452A5",
  "application_id": "F790E7CE7ADB46BBBD56A7CDAA",
  "document_type": "profit_loss",
  "extraction_status": "completed",
  "extracted_data": {
    "q89_last_year_ebitda": "420000",
    "q88_last_year_revenue": "2100000"
  },
  "extracted_summary": {
    "revenue_trend": "Growing 12% YoY",
    "key_risks": "Concentration: top customer is 40% of revenue"
  },
  "timestamp": "2026-05-02T15:32:44.123456+00:00"
}

See Webhooks for the full event catalog and HMAC verification instructions.

Example request

curl -X POST https://api.pgicover.com/api/v2/applications/F790E7CE7ADB46BBBD56A7CDAA/documents/ \
  -H "Authorization: Bearer pk_live_abc123def456ghi789jkl012mno345" \
  -F "file=@/path/to/profit_loss_q1_2026.pdf" \
  -F "documentType=profit_loss"
import requests

with open("/path/to/profit_loss_q1_2026.pdf", "rb") as f:
    resp = requests.post(
        "https://api.pgicover.com/api/v2/applications/F790E7CE7ADB46BBBD56A7CDAA/documents/",
        headers={"Authorization": "Bearer pk_live_abc123def456ghi789jkl012mno345"},
        files={"file": ("profit_loss_q1_2026.pdf", f, "application/pdf")},
        data={"documentType": "profit_loss"},
    )
resp.raise_for_status()
print(resp.json())