Skip to content

Create an offer

POST
/offers
curl --request POST \
--url https://apisandbox.puurk.com/offers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "oid": "example", "type": "template", "lead_id": "example", "title": "Starter Plan", "services": [ { "service_id": "svc_7Hk2LqRm", "quantity": 1, "merge_contract_id": "example" } ], "location_id": "example", "merge_contract_id": "example", "notes": "example" }'

Create a new offer/plan. A client offer (type: client, or type omitted) requires lead_id. A template offer (type: template) is the shareable, client-less plan the provider app calls a “Template”; a lead_id sent with one is ignored, not rejected — the service stores lead_id, lead_name and uid as explicit nulls whatever the payload said.

This endpoint does not price the offer: caller-supplied totals are stored verbatim, so it has no merchant-account precondition.

Request body

Media typeapplication/json

Body for POST /offers. Only oid is required. id and plan_number are server-assigned and ignored if sent, and this endpoint does not price — any totals supplied are stored verbatim.

object
oid
required

The organization the plan belongs to.

string
type

client — a plan for a specific client; requires lead_id. template — a shareable, client-less plan. Omitted or null is treated as client, so intent always comes from this field rather than from the absence of a lead_id.

string
nullable
Allowed values: template client
Example
template
lead_id

Required when type is client or omitted, otherwise the request fails with 400 Missing lead_id. Sent alongside type: template it is ignored — the service stores lead_id, lead_name and uid as explicit nulls.

string
nullable
title

Defaults to Plan <plan_number> when omitted. Must be non-blank if supplied.

string
Example
Starter Plan
services

Service lines to create the plan with, in one call. This is the path that works today for a brand-new plan — use POST /offers/{offerId}/services to add lines to an existing one.

Array<object>
object
service_id
required
string
Example
svc_7Hk2LqRm
quantity
required

Greater than 0. Fractional quantities are allowed.

number
Example
1
merge_contract_id
string
nullable
location_id
string
nullable
merge_contract_id
string
nullable
notes
string
nullable

Offer created

Media typeapplication/json

The created offer, plus the two links POST /offers returns alongside it.

object
id

Unique identifier for the offer

string
type

client — a plan for a specific client; requires lead_id. template — a shareable, client-less plan (the provider app calls these “Templates”); lead_id, lead_name and uid are always null on one.

Null on offers created before the type was recorded, until the backfill stamps them. Newly created offers are never null — an offer created with a lead_id and no type is stored as client.

string
nullable
Allowed values: template client
lead_id

The client this plan is for. Required when type is client or omitted. On a template it is always null in the response: one sent in the request is ignored rather than rejected.

string
nullable
lead_name

Always null on a template offer.

string
nullable
uid

Always null on a template offer.

string
nullable
location_id
string
merge_contract_id
string
merge_contract_payment_total
number
oid
string
organization_name
string
provider_id

Deprecated

string
provider_name

Deprecated

string
providers
Array<string>
title
string
total_price
number
retail_price
number
retail_price_new
number
credit_total
number
credit_total_new
number
deposit_total
number
deposit_total_new
number
photo_url
string
thumbnail_url
string
accepted_at
string
created_at
string
created_by
string
declined_at
string
expired_at
string
pay_in_full_price
number
payment_option
string
Allowed values: finance cash
postpone_reason
string
Allowed values: expensive finance_options questions nervous not_ready time other
status
string
Allowed values: pending accepted declined expired
down_payment
number
tax_total
number
tax_total_new
number
term_cap
number
terms
Array<object>
object
offer_id

Offer these terms belong to

string
uid

User the terms were generated for

string
down_payment

Down payment in cents

number
financed_amount

Amount financed in cents

number
provider_premium_share

Provider’s share of the premium as a fraction between 0 and 1

number
nullable
term_length

Plan length in months

number
monthly_premium

Premium charged per month in cents

number
monthly_provider_premium

Provider share of the monthly premium in cents

number
nullable
monthly_puurk_premium

Puurk share of the monthly premium in cents

number
nullable
monthly_principal

Principal per month in cents

number
monthly_fees

Fees included in each monthly payment in cents

number
nullable
monthly_payout_fees

Payout fees per month in cents

number
nullable
monthly_provider_fees

Provider fees per month in cents

number
nullable
monthly_payment

Monthly payment in cents

number
monthly_opportunity

Monthly opportunity for the provider in cents

number
nullable
total_amount

Total paid over the plan in cents

number
total_premium

Total premium over the plan in cents

number
total_provider_premium

Provider share of the total premium in cents

number
nullable
total_puurk_premium

Puurk share of the total premium in cents

number
nullable
total_principal

Total principal (down payment plus financed amount) in cents

number
total_fees

Total fees over the plan in cents

number
nullable
total_payout_fees

Total payout fees over the plan in cents

number
nullable
total_provider_fees

Total provider fees over the plan in cents

number
nullable
total_opportunity

Total opportunity for the provider in cents

number
nullable
selected_term
number
service_ids
Array<string>
services
Array<object>
object
service_id
string
service_name
string
quantity
number
original_quantity
number
remaining_quantity
number
amount
number
merge_contract_id
string
notes
string
sold_by

User id of the salesperson

string
credit_ids
Array<string>
credits
Array<object>
object
id
string
credit_id
string
credit_name
string
service_id
string
service_name
string
amount
number
percentage
number
calculated_amount
number
merge_contract_id
string
updated_at
string format: date-time
updated_by
string
plan_number
number
shared_at
string format: date-time
shared_by
string
viewed_at
string format: date-time
provider_url

Opens this plan in the provider app, for the organization’s own staff. Do not send it to the client; share client_url instead.

string
client_url

The shareable link to this plan. For a template offer this is the template link — the URL an integrator hands out, since a template has no client of its own. For a client offer it is that client’s plan link.

string
Example
{
"type": "template",
"lead_id": "fLp5zssaLLRcKDbh0DpP",
"lead_name": "John Doe",
"uid": "l8zcL5Va8ei6QQlmhe8n",
"location_id": "g9WWX7DJ0ev6WZ2rOhdQ",
"merge_contract_id": null,
"oid": "iQyjWJf77rrVUx8HJshp",
"organization_name": "Acme Medical",
"provider_id": null,
"provider_name": null,
"providers": [
"4NoHg9a7Yf0QODl1IMcE",
"WKVlP1g5arHJdDa2yBir"
],
"title": "Plan 1234",
"payment_option": "finance",
"postpone_reason": "expensive",
"status": "pending",
"provider_url": "https://provider.puurk.com/plans/plan/abc123",
"client_url": "https://app.puurk.com/offers/offer/abc123"
}

Validation failed (invalid_params). Returned when oid is absent (Missing oid), when lead_id is absent for a client offer (Missing lead_id), when type is not one of template or client, or when any optional string field is present but empty or whitespace-only. A lead_id sent with type: template is not an error — it is ignored.

Media typeapplication/json

The standard error body. error is a stable, machine-readable code — branch on it rather than on message, which is for humans and may change.

object
error

Machine-readable error code.

string
message

Human-readable detail.

string
Example
{
"error": "invalid_params",
"message": "Missing lead_id"
}

Unauthorized

forbidden — the token lacks the write action on offers for this organization. Body validation runs first, so an invalid payload answers 400 before this is reached.

Media typeapplication/json

The standard error body. error is a stable, machine-readable code — branch on it rather than on message, which is for humans and may change.

object
error

Machine-readable error code.

string
message

Human-readable detail.

string
Example
{
"error": "forbidden",
"message": "API client lacks 'write' on 'offers' for organization 'org_123'"
}

lead_not_found when lead_id does not resolve.

Media typeapplication/json

The standard error body. error is a stable, machine-readable code — branch on it rather than on message, which is for humans and may change.

object
error

Machine-readable error code.

string
message

Human-readable detail.

string
Example
{
"error": "lead_not_found",
"message": "No client exists for the supplied lead_id"
}

create_offer_failed — the offer could not be saved.

Media typeapplication/json

The standard error body. error is a stable, machine-readable code — branch on it rather than on message, which is for humans and may change.

object
error

Machine-readable error code.

string
message

Human-readable detail.

string
Example
{
"error": "create_offer_failed",
"message": "Failed to create offer"
}