apicreateproposal JSON request body proposal + entries contact/product objects supported
POST /api/createproposal creates a proposal, its proposal entries, optional contact, and optional embedded products.
Complete field reference — full maximal JSON schema with every accepted attribute, enum values, defaults, and explanations.
This endpoint creates the same core records used by the editor: one row in proposals and one or more rows in entries. After entries are created, the proposal totals are recalculated.
Top-level JSON fields
keyYour API key.
proposalProposal fields can be sent inside proposal, or directly at top level. If both are used, proposal is used for proposal fields.
contactContact linked to the proposal. Supported formats:
- Existing contact id:
123 - Contact object accepted by
/api/createcontact: creates/updates contact first, then uses itsname,code, andpersonon the proposal.
entriesFlat list of proposal lines.
groupsGrouped proposal lines. Each group creates a group_sep entry and then creates the group's entries.
settingsProposal-specific settings saved in the settings table using the proposal id. Examples: advanceper, showgroup, selectoff, textcolor. showgroup is returned by getproposals. selectoff: 1 hides parent SELECT! buttons. There is no showvat/showtax key; 0% VAT hides the VAT row automatically. Do not send tunc — that is a company setting, not a proposal setting.
Proposal Fields
You can send any standard proposal field such as:
namerequirednris always generated server-side. Any submittednris ignored.typedefaults to0.urlis accepted only whentype = 1. For defaulttype = 0and every other proposal type, URL is always generated server-side.created,deadline,currency,languageauthorandowneras user ids; can be sent directly at top level or insideproposal. If omitted, the API falls back to the default user logic.discount,status,comment,confirmable
Entry Fields
Each entry can contain direct entries table fields:
product: line title/text. If a product object/id/SKU is supplied, this defaults to the product name.product_ref: product id, SKU, or product object.link: product id. Used likeproduct_refwhen numeric.sku,ammount,ammount2,unit,unit2price,prime,discount,pvm,suppliercomment,notes,image,tagsattribute: usetext_sep,header,footer, or omit/"0"for normal product rows (empty is stored as"0").accepted:1marks a selectable row as selected/accepted.selectable: child rows only.1makes an included child individually toggleable on the public offer. Does not hide the parent SELECT! button. Usesettings.selectoff: 1to hide parent SELECT!.children: array of child entries. Child rows are linked to the parent row through the existingparent/orderpsystem.
Select controls (selectable, selectoff, tunc)
These three names are easy to mix up:
selectableis an entry flag for child rows only.1gives that included child its own SELECT toggle on the public offer.0means the child is not individually toggleable (mandatory included / packing). Sendingselectable: 0on a top-level / parent product does not hide the green SELECT! button.getproposalsmay still returnselectable: 0on parents; that value is unused for parent SELECT!.selectoffis a proposal setting (settings.selectoffor top-levelselectoff).0(default) shows parent SELECT! / accept buttons.1hides them (editor label: “Hide select button”). This is the only create/editproposal way to hide parent SELECT!. It does not remove childselectable: 1toggles.tuncis a company setting (“Separate prices for included products”). It is not a proposal setting. Do not sendsettings.tuncas a workaround: the public offer and/acceptingread company settings, so a proposal-leveltunchas no effect.tuncdoes not control group subtotals or the sticky/footer total (showgroupshows group subtotals). When companytuncis on and the parent hasaddup: 1, a bundle sumbox is shown next to included children.
Product Reference Formats
Entries can link to existing products or create products inline.
Existing product by id
{
"product_ref": 15,
"ammount": 2
}
Existing product by SKU
{
"product_ref": "SKU-001",
"ammount": 2
}
Create product inline
The object uses the same product payload as /api/createproduct, without the API key.
{
"product_ref": {
"name": "Created inline product",
"sku": "INLINE-001",
"category": "API products",
"price": {
"Supplier A": {
"1": [50, 75, 0]
}
}
},
"ammount": 1
}
Feature Values on Entries
You can save entry-level feature selections using feature ids:
{
"product_ref": "SKU-001",
"feature_values": {
"12": [44, 45],
"15": "custom text"
},
"feature_custom_text": {
"15": "custom text"
},
"feature_custom_number": {
"20": "5"
}
}
Minimal Example
{
"key": "YOUR_API_KEY",
"name": "API proposal",
"author": 3,
"owner": 3,
"contact": {
"name": "Example Company UAB",
"code": "123456789",
"email": "info@example.com"
},
"entries": [
{
"product_ref": "SKU-001",
"ammount": 2,
"children": [
{
"product_ref": "INCLUDED-SKU-001",
"ammount": 1,
"selectable": 1,
"accepted": 1
}
]
}
]
}
Grouped Full Example
{
"key": "YOUR_API_KEY",
"proposal": {
"name": "Website project",
"currency": "EUR",
"discount": 5,
"comment": "Created through API"
},
"contact": {
"name": "Client Company",
"code": "300000001",
"phone": "+37060000000",
"meta": {
"city": "Vilnius",
"website": "https://client.example"
}
},
"settings": {
"advanceper": 30,
"showgroup": 1
},
"groups": [
{
"name": "Main work",
"advance": 30,
"entries": [
{
"product_ref": {
"name": "Design work",
"sku": "DESIGN-API-001",
"category": "Services",
"price": {
"Supplier A": {
"1": [0, 500, 0]
}
}
},
"ammount": 1,
"comment": "Design stage"
},
{
"product": "Project note",
"attribute": "text_sep",
"comment": "<p>This is an informational row.</p>"
}
]
},
{
"name": "Recurring",
"entries": [
{
"product_ref": "HOSTING-001",
"ammount": 12,
"unit": "mėn",
"periodical": 1
}
]
}
]
}
Success Response
{
"status": "ok",
"data": {
"proposal": { "id": 123, "name": "Website project", "url": "abc123..." },
"entries": [ /* created entries */ ],
"url": "https://your-domain/offer/abc123..."
}
}
Error Responses
{ "status": "error", "data": "api disabled" }{ "status": "error", "data": "no key" }{ "status": "error", "data": "wrong key" }{ "status": "error", "data": "name required" }{ "status": "error", "data": "entries required" }{ "status": "error", "data": "contact not found" }