API reference
Three extraction endpoints, one response shape. Base URL for all requests is https://www.procuremind.io. New here? Start with the developer overview.
Authentication
POST /api/extract accepts an API key in the X-API-Key header (Pro/Team plans), a Supabase session token as Authorization: Bearer, or a dashboard cookie session.
The file endpoints (/api/extract/pdf, /api/extract/excel, /api/extract/docx, /api/extract/image) currently authenticate with Authorization: Bearer or a cookie session only. API-key auth is not yet enabled on file uploads. File upload also requires a paid plan.
All responses are JSON. Errors return { "error": "..." } with the status codes listed per endpoint.
POST/api/extractExtract structured data from a quote email's text: the developer-API endpoint.
Request
Content-Type: application/json · Auth: X-API-Key (Pro/Team), Bearer token, or cookie session
| Field | Type | Description |
|---|---|---|
email_body | string | Required. The plain-text body of the quote email. Maximum 100 KB. |
email_subject | string | Optional. Email subject line: improves document-type and quote-number detection. |
email_sender | string | Optional. Sender address: used for vendor detection and duplicate matching. |
Response: 200 OK
| Field | Type | Description |
|---|---|---|
extraction_id | string (uuid) | ID of the saved extraction. Reference it in support requests. |
extracted_data | object | The extracted fields (see the extracted_data schema below). |
confidence_score | number | 0–1 confidence score (same value as extracted_data.confidence_score). |
model_used | string | AI model that produced the extraction (e.g. gpt-4o-mini). |
tokens_used | number | Tokens consumed by the extraction. Absent on cached responses. |
cached | boolean | Present and true when the same subject + sender + body was extracted before. The stored result is returned without consuming quota. |
Example
curl -X POST https://www.procuremind.io/api/extract \
-H "Content-Type: application/json" \
-H "X-API-Key: pm_your_key_here" \
-d '{
"email_subject": "Quote Q-1042 - Acme Fasteners",
"email_sender": "sales@acme-fasteners.com",
"email_body": "Hi, please find our quote: 500x M6 hex bolts @ $0.12 each..."
}'Errors
| Status | When |
|---|---|
400 | Invalid JSON body, email_body missing, or body larger than 100 KB. |
401 | No valid API key, Bearer token, or session. |
402 | Monthly extraction limit reached. Response includes limit, current_count and upgrade_url. |
403 | Valid API key on a Free or Starter plan. API access requires Pro or Team. |
429 | Rate limit exceeded (10/min per user). Retry after the number of seconds in the Retry-After header. |
500 | Extraction or persistence failure; safe to retry. |
POST/api/extract/pdfExtract structured data from a PDF quote attachment.
Request
Content-Type: multipart/form-data · Auth: Authorization: Bearer, X-API-Key (Pro/Team) or cookie session · Paid plans only
| Field | Type | Description |
|---|---|---|
file | file | Required. The PDF document, sent as the multipart field named "file". Must be application/pdf, under 10 MB. |
Text-based PDFs are parsed directly; scanned PDFs fall back to OCR, and low-confidence results are retried with vision extraction in batches of 5 pages, merged into a single result. Long text documents are chunked automatically with the same merge. The source field in the response tells you which path produced the result.
Scanned-document page caps per plan (vision OCR):
| Plan | Max pages per scanned document |
|---|---|
| Free | 5 (file upload itself requires a paid plan) |
| Starter | 10 |
| Pro | 50 |
| Team | 100 |
Response: 200 OK
Same fields as POST /api/extract, plus:
| Field | Type | Description |
|---|---|---|
source | string | Extraction path: pdf_text, pdf_ocr, or pdf_vision. |
page_count | number | Number of pages read from the PDF. |
pages_processed | number | Present on pdf_vision results: pages actually processed by vision (equals page_count unless processing stopped early). |
partial | boolean | Present and true when the time budget stopped chunked processing early — the result covers pages_processed of page_count pages. |
Example
curl -X POST https://www.procuremind.io/api/extract/pdf \
-H "Authorization: Bearer <session token>" \
-F "file=@quote.pdf;type=application/pdf"Errors
| Status | When |
|---|---|
400 | No file field, file is not application/pdf, or the file fails PDF validation. |
401 | No valid Bearer token or session. |
402 | Free plan (PDF upload requires a paid plan), or monthly extraction limit reached. |
413 | File larger than 10 MB. |
422 | PDF could not be read or contains no readable text — or the document exceeds your plan's page cap for scanned documents (response includes pages, page_cap and upgrade_url). |
429 | Rate limit exceeded (3/min per user). See the Retry-After header. |
500 | Extraction failure; safe to retry. |
504 | OCR exceeded the processing time limit; try a text-based PDF or fewer pages. |
POST/api/extract/excelExtract structured data from an Excel or CSV quote file.
Request
Content-Type: multipart/form-data · Auth: Authorization: Bearer, X-API-Key (Pro/Team) or cookie session · Paid plans only
| Field | Type | Description |
|---|---|---|
file | file | Required. The spreadsheet, sent as the multipart field named "file". Supported: .xlsx, .xls, .csv, under 10 MB. |
Response: 200 OK
Same fields as POST /api/extract, plus:
| Field | Type | Description |
|---|---|---|
source | string | Always excel. |
sheet_count | number | Number of sheets read from the workbook. |
row_count | number | Number of rows read. |
Example
curl -X POST https://www.procuremind.io/api/extract/excel \
-H "Authorization: Bearer <session token>" \
-F "file=@quotes.xlsx"Errors
| Status | When |
|---|---|
400 | No file field, unsupported file type (only .xlsx, .xls, .csv), or the file fails validation. |
401 | No valid Bearer token or session. |
402 | Free plan (file upload requires a paid plan), or monthly extraction limit reached. |
413 | File larger than 10 MB. |
422 | The file could not be read as a spreadsheet. |
429 | Rate limit exceeded (3/min per user). See the Retry-After header. |
500 | Extraction failure; safe to retry. |
POST/api/extract/docxExtract structured data from a Word (.docx) quote document.
Request
Content-Type: multipart/form-data · Auth: Authorization: Bearer, X-API-Key (Pro/Team) or cookie session · Paid plans only
| Field | Type | Description |
|---|---|---|
file | file | Required. The Word document, sent as the multipart field named "file". Only .docx, under 10 MB — validated by extension and by content (a .docx is a ZIP container, checked by signature). |
Text is extracted from the document and run through the same pipeline as email extraction — document classification, fields and confidence scoring are identical.
Response: 200 OK
Same fields as POST /api/extract, plus:
| Field | Type | Description |
|---|---|---|
source | string | Always docx. |
Example
curl -X POST https://www.procuremind.io/api/extract/docx \
-H "Authorization: Bearer <session token>" \
-F "file=@quote.docx"Errors
| Status | When |
|---|---|
400 | No file field, file is not a .docx, or the file fails validation. |
401 | No valid Bearer token or session. |
402 | Free plan (file upload requires a paid plan), or monthly extraction limit reached. |
413 | File larger than 10 MB. |
422 | The document could not be read, or contains no readable text. |
429 | Rate limit exceeded (3/min per user). See the Retry-After header. |
500 | Extraction failure; safe to retry. |
POST/api/extract/imageExtract structured data from a photo or screenshot of a quote.
Request
Content-Type: multipart/form-data · Auth: Authorization: Bearer, X-API-Key (Pro/Team) or cookie session · Paid plans only
| Field | Type | Description |
|---|---|---|
file | file | Required. The image, sent as the multipart field named "file". Supported: .png, .jpg, .jpeg, .webp, under 10 MB. The real format is detected from the file content (magic bytes), not the extension. |
Images go straight to vision extraction — one image is one extraction, counted against the same monthly quota as everything else.
Response: 200 OK
Same fields as POST /api/extract, plus:
| Field | Type | Description |
|---|---|---|
source | string | Always image. |
Example
curl -X POST https://www.procuremind.io/api/extract/image \
-H "Authorization: Bearer <session token>" \
-F "file=@quote.jpg"Errors
| Status | When |
|---|---|
400 | No file field, unsupported extension (only .png, .jpg, .jpeg, .webp), or the file is not a valid image. |
401 | No valid Bearer token or session. |
402 | Free plan (file upload requires a paid plan), or monthly extraction limit reached. |
413 | File larger than 10 MB. |
429 | Rate limit exceeded (3/min per user). See the Retry-After header. |
500 | Extraction failure; safe to retry. |
The extracted_data object
All three endpoints return the same schema. Fields that don't apply to a document type come back as empty strings.
| Field | Type | Description |
|---|---|---|
document_type | string | One of quote, po, bom, rfi. Defaults to quote. |
vendor_name | string | Supplier name. |
vendor_email | string | Supplier email address. |
buyer_name | string | Issuing organisation (PO, BOM, RFI; blank for quotes). |
buyer_email | string | Buyer email (PO, BOM, RFI; blank for quotes). |
delivery_address | string | Ship-to address (PO only). |
quote_number | string | Document reference: quote number, PO number, BOM ID or RFI reference, depending on document_type. |
bom_version | string | BOM revision, e.g. "Rev B" (BOM only). |
currency | string | Document currency code. |
total_amount | string | Grand total, as a string. |
subtotal | string | Pre-tax subtotal. |
tax_amount | string | Tax amount (e.g. GST/VAT). |
shipping_amount | string | Shipping/freight amount. |
due_date | string | Due or delivery date. |
quote_validity | string | Quote/RFI response deadline (blank for BOM/PO). |
payment_terms | string | Payment terms (quote and PO only). |
incoterms | string | Incoterms (quote and PO only). |
items | LineItem[] | Line items (see the LineItem table below). |
confidence_score | number | 0–1 confidence score for the extraction. |
items[]: the LineItem object
| Field | Type | Description |
|---|---|---|
description | string | Item description. |
sku | string | Part number / SKU / product code. |
quantity | string | Quantity ordered/quoted. |
unit | string | Unit of measure (each, kg, m, box…). |
unit_price | string | Price per unit. |
line_total | string | Extended line total. |
currency | string | Per-line currency (may differ from document currency). |
discount | string | Discount, e.g. "10%" or "50.00". |
moq | string | Minimum order quantity. |
spq | string | Standard pack quantity. |
lead_time | string | Lead time, e.g. "4 weeks". |
manufacturer | string | Manufacturer name (BOM/PO extended fields). |
manufacturer_pn | string | Manufacturer part number (MPN). |
bom_level | string | Hierarchy depth in a BOM, e.g. "1.1". |
reference_designator | string | PCB/schematic references, e.g. "R1, R2, C3" (BOM). |
notes | string | Line-level notes or RFI question-response text. |
Webhooks
Pro and Team accounts can register webhook endpoints in the dashboard under Settings → Webhooks. After every successful extraction, ProcureMind POSTs a signed extraction.completed event to each active endpoint.
Each endpoint gets a signing secret (whsec_…) shown once at creation. Verify deliveries by recomputing HMAC-SHA256(secret, "{timestamp}.{raw body}") and comparing it to the v1 value in the X-ProcureMind.io-Signature header. Delivery times out after 10 seconds and is fire-and-forget. Webhook failures never affect the extraction response.
POST https://your-server.example/hooks/procuremind
Content-Type: application/json
X-ProcureMind.io-Event: extraction.completed
X-ProcureMind.io-Signature: t=1756723200,v1=9f86d081...
{
"event": "extraction.completed",
"created_at": "2026-09-01T12:00:00.000Z",
"data": {
"extraction_id": "9f1c2a7e-...",
"document_type": "quote",
"vendor_name": "Acme Fasteners",
"total_amount": "60.00",
"currency": "USD",
"confidence_score": 0.97,
"source": "email",
"sent_to_sheet": false
}
}Questions?
Email support@procuremind.io and include the extraction_id: we can trace the request exactly.