https://www.helprange.com/api/v2/offersCreates an offer with its own link: based on a folder (folder_id – new files show up), from documents (file_ids), or empty with just a title and client. Offers without a folder get their own folder "Offer - <title>". The API never e-mails the client: send the returned url yourself.
Plans: Max, Unlimited, Deal Room Pass, trial. Auth: Authorization: Bearer hr_live_… (team key), X-API-Key, Authorization: Api-Key <key> or an OAuth Bearer token. Scope: links:write. Operation id: createOffer
Content types: application/json, application/x-www-form-urlencoded.
| Field | Type | Description |
|---|---|---|
folder_id | integer | Base the offer on this folder. |
file_ids | array of integer | Documents (a JSON list, repeated form fields or "1,2"). |
status | "active" | "draft" | Default active. |
title | string | What the client sees as the title. Create: default the folder's / first document's name. |
client_first_name | string | |
client_last_name | string | |
client_company | string | |
client_email | string | The client's e-mail – never e-mailed by the API. |
message | string | Message on the offer page. (max length 2000) |
price_summary | string | One line, e.g. "3 packages, from 2,400 EUR". |
show_accept | boolean | "Accept offer" button (default on). |
accept_label | string | |
show_contact | boolean | "Request contact" button (default on). |
contact_label | string | |
show_decline | boolean | "Decline" button with an optional reason (default off). |
decline_label | string | |
require_email_verification | boolean | The client verifies their e-mail with a one-time code (default on). |
access | "any" | "email" | "domain" | With e-mail verification: anyone, only client_email, or client_email and colleagues at the same domain. |
allow_download | boolean | |
watermark | boolean | |
expires_on | string | Last day, YYYY-MM-DD ("" removes it). |
passcode | string | Asked before the offer opens ("" removes it). |
screenshot_protection | boolean | Pro and higher (else 402). |
one_viewer_at_a_time | boolean | |
open_directly | boolean | One-file offers: open the document directly instead of the offer page. |
layout | "classic" | "split" | "cover" | "minimal" |
curl -X POST https://www.helprange.com/api/v2/offers \
-H "X-API-Key: $HELPRANGE_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary '{"title": "Fit-out Q1", "file_ids": [250, 251], "client_company": "Acme", "client_email": "anna@acme.com", "access": "domain", "show_decline": true, "passcode": "oak-41", "expires_on": "2026-12-31"}'
const res = await fetch('https://www.helprange.com/api/v2/offers', {
method: 'POST',
headers: {'X-API-Key': process.env.HELPRANGE_API_KEY, 'Content-Type': 'application/json'},
body: JSON.stringify({
"title": "Fit-out Q1",
"file_ids": [
250,
251
],
"client_company": "Acme",
"client_email": "anna@acme.com",
"access": "domain",
"show_decline": true,
"passcode": "oak-41",
"expires_on": "2026-12-31"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.error || data.message || data.reason}`);
console.log(data);
import os
import requests
r = requests.post(
'https://www.helprange.com/api/v2/offers',
headers={'X-API-Key': os.environ['HELPRANGE_API_KEY']},
json={
"title": "Fit-out Q1",
"file_ids": [250, 251],
"client_company": "Acme",
"client_email": "anna@acme.com",
"access": "domain",
"show_decline": True,
"passcode": "oak-41",
"expires_on": "2026-12-31",
},
timeout=60,
)
r.raise_for_status()
print(r.json())
The samples read the key from the HELPRANGE_API_KEY environment variable.
| Status | Description |
|---|---|
201 | Created |
400 | Invalid input – the message says which parameter. |
401 | Missing or invalid API key / OAuth token. Team keys: code invalid_key, key_revoked, key_expired or key_owner_removed. |
402 | Your plan does not include the REST API (or this feature). |
403 | The team API key lacks the scope this endpoint needs. |
404 | Not found – or not yours (the API never says which). |
405 | Wrong HTTP method for this endpoint. |
429 | A usage limit was reached – wait Retry-After seconds. |
{
"id": 31,
"title": "Fit-out Q1",
"status": "active",
"expired": false,
"client": {
"first_name": "Anna",
"last_name": "Nowak",
"company": "Acme",
"email": "anna@acme.com"
},
"kind": "files",
"url": "https://www.helprange.com/o/Xk2pQ9/",
"document_views": 4,
"page_visits": 2,
"last_viewed_at": "2026-11-11T09:12:00+00:00",
"sent_at": null,
"created_at": "2026-11-11T08:00:00+00:00",
"updated_at": "2026-11-11T08:00:00+00:00",
"stored_status": "active",
"public_url": "https://www.helprange.com/o/Xk2pQ9/",
"panel_url": "https://www.helprange.com/user/offers/31/",
"message": "Hi Anna, here is our proposal.",
"price_summary": "from 2,400 EUR",
"buttons": {
"accept": {
"shown": true,
"label": ""
},
"contact": {
"shown": true,
"label": ""
},
"decline": {
"shown": true,
"label": ""
}
},
"protection": {
"require_email_verification": true,
"access": "domain",
"allowed_emails": "anna@acme.com @acme.com",
"allow_download": false,
"watermark": false,
"passcode": true,
"screenshot_protection": false,
"one_viewer_at_a_time": false,
"expires_on": "2026-12-31"
},
"open_directly": false,
"layout": "classic",
"theme": "light",
"base_folder": null,
"own_folder": {
"id": 77,
"name": "Offer - Fit-out Q1"
},
"files": [
{
"id": 250,
"name": "proposal.pdf",
"folder_id": 77,
"source": "own_folder",
"view_url": "https://www.helprange.com/view/5b0c.../proposal.pdf",
"views": 3
}
],
"people": 1,
"engagement": 82,
"responses_count": 1,
"responses": [
{
"kind": "declined the offer",
"type": "decline",
"offer_id": 31,
"email": "anna@acme.com",
"email_verified": true,
"offer": "Fit-out Q1",
"comment": "Too expensive",
"document_id": null,
"engagement": 64,
"country": "PL",
"at": "2026-11-11T10:00:00+00:00"
}
],
"sent_to": null,
"created_by": "ann@acme.com",
"legacy_url": null,
"send_by_email": "HelpRange does not e-mail offers through the API or MCP. Send the offer link (url) yourself, or use \"Send by e-mail\" on the offer page in HelpRange."
}
Uses your real account. The request goes from your browser straight to www.helprange.com with your API key and acts as you – this POST changes data and may e-mail people. The key stays in this page (in this tab's session storage only if you tick the box), is never put in a URL and is not sent to analytics. Do not paste a key on a shared computer.