MCP tools
Quotations and sales people
MCP tools for creating, editing, approving, generating and downloading quotations, and for the sales-person roster.
A quotation is the priced offer your organization presents to a buyer: a header (the addressee, validity, delivery period, payment terms and a contact person) and lines with quantity, unit price and VAT. It stays a draft until it is approved. The organization stamp and the sales person's signature print on the PDF only while the quotation is approved, and any content change returns it to draft. The sales-people roster is the list a quotation's printed contact person is picked from. The app's own pages are described in Quotations and Sales people.
| Tool | What it does |
|---|---|
| quotations_list | List quotations |
| quotations_get | Get a quotation |
| quotations_create | Create a quotation |
| quotations_update | Update a quotation |
| quotations_delete | Delete a quotation |
| quotations_bulk_update | Update many quotation headers |
| quotations_set_approval | Approve or un-approve quotations |
| quotations_defaults_get | Get the quotation defaults |
| tender_quotations_list | List a tender's quotations |
| tender_quotations_generate | Generate a tender's quotations |
| tender_quotation_pdf_bundle | Download a tender's quotation PDFs |
| quotation_pdf | Download a quotation PDF |
| quotation_package | Download a quotation package |
| quotation_packages_bundle | Download many quotation packages |
| quotation_product_document_prefill | Get the product document prefill |
| quotation_product_document | Generate the product document |
| sales_persons_list | List sales people |
| sales_persons_get | Get a sales person |
| sales_persons_create | Add a sales person |
| sales_persons_update | Update a sales person |
| sales_persons_delete | Delete a sales person |
Quotations
Creating, reading, editing and deleting quotations, approving them, and reading the defaults new quotations start from.
quotations_list
Lists your organization's quotations, newest first, optionally only those on one tender. summary adds the grand totals per currency.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | No | Only the quotations on this tender. |
summary | boolean | No | true adds a summary to the reply: the quotation count and the grand total per currency in totals_by_currency, over every matching quotation rather than only the page. |
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
Returns: A page of quotations in data with total, page, page_size and total_pages, plus summary when asked. Each quotation carries id, quote_number, customer_name, approval_status, currency, the totals total_subtotal, total_vat and total_grand, and its tender in tenders.
- The list does not carry the lines; use
quotations_getfor them.
quotations_get
Reads one quotation in full: the header, the lines, the company details frozen at creation, the printed contact person and the linked tender. Use it before an edit to see exactly what the quotation holds.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
Returns: quotation (the header, approval_status, approved_at, salesperson_snapshot and company_snapshot, whose logo_url and stamp_url are freshly signed links), items with each line's ID, tender (agency_name and tender_name, or null), and itemImages with the cover photo link of each line whose product has photos.
- A line's ID here is what
quotation_product_documenttakes asitem_idand as the keys offields. - A quotation that is not your organization's answers 404.
quotations_create
Creates one quotation: either standalone, with an addressee you name, or linked to a tender whose agency becomes the addressee. It gets a number from your organization's counter, starts as a draft, and your company details are frozen into it at that moment.
Changes data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | No | The ID of the tender to link; its agency becomes the addressee unless you name another. It must be a tender your organization can see. Leave it out or send null for a quotation with no tender. |
customer_id | integer | No | The ID of a customer profile from customers_list. Its name prints as the addressee when you give no customer_name. A customer that is not your organization's answers 404. |
customer_name | string | No | The printed addressee, up to 200 characters. It wins over the linked tender's agency name. On a quotation with no tender, give this or customer_id so the quotation has an addressee. |
rfq_no | string | No | The buyer's own RFQ number, up to 100 characters. |
quote_validity_days | integer | No | How many days the offer stays valid, 1 to 3650. When left out, your organization's default applies (see quotations_defaults_get). |
delivery_period_days | integer | No | The delivery period in days, 1 to 3650. When left out, your organization's default applies. |
payment_terms | string | No | The payment terms printed on the quotation, up to 500 characters: one of the presets advance_100, advance_50_balance_on_delivery, on_delivery, net_30, net_60 or net_90 (printed in Arabic and English), or free text printed as typed. When left out, your organization's default terms apply; null prints none. |
currency | string | No | The currency code, 1 to 8 characters. Defaults to SAR. |
notes | string | No | Notes printed on the quotation, up to 2,000 characters. When left out, the standard closing line "Looking forward for your business" prints. |
sales_person_id | integer | No | The ID of a person on your sales roster, from sales_persons_list. Their details fill any salesperson_* field you leave out, and the quotations list can filter by them. An ID that is not on your roster answers sales_person_not_found. |
salesperson_name | string | No | The name printed as the contact person, up to 200 characters. When left out, the picked sales person's name is used, else the name of the member who connected. null prints none. |
salesperson_phone | string | No | The phone printed for the contact person, up to 50 characters. When left out, the picked sales person's phone is used, else the connected member's. null prints none. |
salesperson_email | string | No | The email printed for the contact person, a valid address up to 320 characters. When left out, the picked sales person's email is used, else the connected member's. null prints none. |
items | object[] | Yes | The quotation lines, from 1 to 500. |
items[].tender_item_id | integer | No | The ID of the tender item this line covers, from the tender's items in tenders_get. Used only on a quotation linked to a tender. |
items[].product_id | integer | No | The ID of the catalog product behind this line, from products_list. A linked product supplies the catalog number when you leave it blank and the VAT rate when you leave it out, and later feeds the product document with its catalog and certificates. |
items[].manufacturer_id | integer | No | The ID of the manufacturer this line is quoted under, from manufacturers_list; it must be one of your organization's. When left out, the product's default manufacturer is used, and null keeps it automatic, so the manufacturer is chosen when documents are generated. It decides the manufacturer name, the authorization letter and the agency type in the product document. |
items[].description | string | Yes | The line's description as printed, 1 to 4,000 characters. |
items[].stock_no | string | No | The stock or NUPCO material number, up to 100 characters. |
items[].catalog_no | string | No | The catalog number, up to 100 characters. |
items[].unit | string | No | The unit of measure, up to 50 characters. |
items[].quantity | number | Yes | The quantity, zero or more. |
items[].unit_price | number | Yes | The unit price in the quotation's currency, zero or more. |
items[].vat_rate | number | No | The VAT percentage, 0 to 100. When left out it is 0 for a line whose linked product is medical and 15 for everything else, including a line with no product. |
{
"tender_id": 48213,
"rfq_no": "RFQ-2026-0417",
"quote_validity_days": 90,
"delivery_period_days": 30,
"payment_terms": "net_30",
"sales_person_id": 6,
"items": [
{
"tender_item_id": 902114,
"product_id": 3381,
"description": "Disposable syringe 5 ml, Luer lock, sterile",
"stock_no": "4111610529900",
"unit": "Box",
"quantity": 200,
"unit_price": 24.5
}
]
}Returns: The new quotation_id, the quote_number it was given, and logo_url and stamp_url links for the logo and the stamp.
- Creating a quotation counts against your plan's monthly quota: 50 on Starter, unlimited on Pro. At the limit the tool answers
plan_limit_reachedwithlimitandcurrent. - Blank fields are filled from your own data: a line tied to a tender item and carrying no product takes the product you selected for that item, a blank catalog number comes from the product, and a blank stock number comes from the tender item's material code. What you typed is never replaced.
- A tender your organization cannot see answers 404, and a manufacturer that is not your organization's answers
manufacturer_not_found.
quotations_update
Edits a quotation's header or lines. Every field is optional and whatever you leave out stays as it is, so a header-only edit needs no lines. Any change returns the quotation to draft.
Changes data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
quote_number | string | No | The number printed on the quotation, 1 to 100 characters. Unique within your organization. |
tender_id | integer | No | Links the quotation to this tender, one your organization can see. Send null to unlink it. |
customer_id | integer | No | Links the quotation to a customer profile, or null to unlink. This sets the link only; to change the printed addressee, send customer_name. |
customer_name | string | No | The printed addressee, up to 200 characters. Send null to fall back to the linked tender's agency. |
rfq_no | string | No | The buyer's own RFQ number, up to 100 characters. Send null to clear it. |
quote_validity_days | integer | No | How many days the offer stays valid, 1 to 3650. Send null to clear it. |
delivery_period_days | integer | No | The delivery period in days, 1 to 3650. Send null to clear it. |
payment_terms | string | No | The payment terms printed on the quotation, up to 500 characters: one of the presets advance_100, advance_50_balance_on_delivery, on_delivery, net_30, net_60 or net_90 (printed in Arabic and English), or free text printed as typed. Send null to print no payment terms. |
currency | string | No | The new currency code, 1 to 8 characters. |
notes | string | No | Notes printed on the quotation, up to 2,000 characters. Send null to clear them. |
sales_person_id | integer | No | The ID of a person on your sales roster, from sales_persons_list. Their details fill any salesperson_* field you do not send in the same call, and the quotations list can filter by them. Send null to remove the link; the printed contact stays as it is. |
salesperson_name | string | No | The name printed as the contact person, up to 200 characters. It is merged into the stored contact, so what you do not send stays. Send null to print no name. |
salesperson_phone | string | No | The phone printed for the contact person, up to 50 characters. Send null to print no phone. |
salesperson_email | string | No | The email printed for the contact person, a valid address up to 320 characters. Send null to print no email. |
items | object[] | No | The complete new set of lines, 1 to 500. When sent, it replaces every existing line and the totals are recomputed; when left out, the lines and totals stay as they are. |
items[].tender_item_id | integer | No | The ID of the tender item this line covers, from the tender's items in tenders_get. Used only on a quotation linked to a tender. |
items[].product_id | integer | No | The ID of the catalog product behind this line, from products_list. A linked product supplies the catalog number when you leave it blank and the VAT rate when you leave it out, and later feeds the product document with its catalog and certificates. |
items[].manufacturer_id | integer | No | The ID of the manufacturer this line is quoted under, from manufacturers_list; it must be one of your organization's. When left out, the product's default manufacturer is used, and null keeps it automatic, so the manufacturer is chosen when documents are generated. It decides the manufacturer name, the authorization letter and the agency type in the product document. |
items[].description | string | Yes | The line's description as printed, 1 to 4,000 characters. |
items[].stock_no | string | No | The stock or NUPCO material number, up to 100 characters. |
items[].catalog_no | string | No | The catalog number, up to 100 characters. |
items[].unit | string | No | The unit of measure, up to 50 characters. |
items[].quantity | number | Yes | The quantity, zero or more. |
items[].unit_price | number | Yes | The unit price in the quotation's currency, zero or more. |
items[].vat_rate | number | No | The VAT percentage, 0 to 100. When left out it is 0 for a line whose linked product is medical and 15 for everything else, including a line with no product. |
{
"id": 311,
"customer_name": "Acme Medical Supplies",
"quote_validity_days": 60,
"payment_terms": null
}Returns: The quotation_id of the edited quotation.
- Any change to the header or the lines returns the quotation to draft, so the organization stamp and the signature stop printing until you approve it again with quotations_set_approval.
- Sending
itemsreplaces every line, so a partial array truncates the document. Unlike creation, an edit does not fill a blank catalog number or stock number from your data, so send both in full with each line. An omitted VAT rate and an omitted manufacturer are resolved as on creation. - A number already taken answers 409
quote_number_taken. A quotation that is not your organization's, or a tender it cannot see, answers 404. - The frozen company details are never changed by an edit.
quotations_delete
Deletes a quotation and its lines for good. This cannot be undone.
Deletes or overwrites data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
Returns: A confirmation, ok: true.
- A quotation that is not your organization's answers 404.
quotations_bulk_update
Edits the headers of up to 200 quotations in one call: number, addressee, validity, delivery, currency, notes, tender link, sales person and contact details. It takes no lines, so it can never truncate a document; to change lines, use quotations_update per quotation.
Changes data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
updates | object[] | Yes | One element per quotation, from 1 to 200. Each carries the id and only the keys to change: what is left out stays, and an explicit null clears the field. |
updates[].id | integer | Yes | The quotation's ID, from quotations_list. |
updates[].quote_number | string | No | The new number, 1 to 100 characters. A number already taken fails that row alone. |
updates[].tender_id | integer | No | Links the quotation to this tender. Send null to unlink. |
updates[].customer_name | string | No | The printed addressee, up to 200 characters. Send null to fall back to the tender's agency. |
updates[].rfq_no | string | No | The buyer's own RFQ number, up to 100 characters. Send null to clear it. |
updates[].quote_validity_days | integer | No | How many days the offer stays valid, 1 to 3650. Send null to clear it. |
updates[].delivery_period_days | integer | No | The delivery period in days, 1 to 3650. Send null to clear it. |
updates[].payment_terms | string | No | The payment terms printed on the quotation, up to 500 characters: one of the presets advance_100, advance_50_balance_on_delivery, on_delivery, net_30, net_60 or net_90 (printed in Arabic and English), or free text printed as typed. Send null to print no payment terms. |
updates[].currency | string | No | The new currency code, 1 to 8 characters. |
updates[].notes | string | No | Notes printed on the quotation, up to 2,000 characters. Send null to clear them. |
updates[].sales_person_id | integer | No | The ID of a person on your sales roster, from sales_persons_list. Their details fill any salesperson_* field you do not send in the same call, and the quotations list can filter by them. Send null to remove the link; the printed contact stays as it is. |
updates[].salesperson_name | string | No | The name printed as the contact person, up to 200 characters. It is merged into the stored contact, so what you do not send stays. Send null to print no name. |
updates[].salesperson_phone | string | No | The phone printed for the contact person, up to 50 characters. Send null to print no phone. |
updates[].salesperson_email | string | No | The email printed for the contact person, a valid address up to 320 characters. Send null to print no email. |
{
"updates": [
{
"id": 311,
"sales_person_id": 6,
"payment_terms": "net_30"
},
{
"id": 312,
"sales_person_id": 6,
"payment_terms": "net_30"
}
]
}Returns: updated, the IDs of the quotations that changed, and failed, a list of id and error for the rows that did not.
- Rows are applied independently and the call succeeds even when some fail: what succeeded stays saved, and each failed row is named with its reason, such as
quote_number_takenorsales_person_not_found. Always readfailed. - Every quotation that changed returns to draft, so approve it again with quotations_set_approval.
- To present several quotations under one person, repeat the same
sales_person_idon every row: their name, phone and email fill each printed contact block, and their signature prints once the quotations are approved again.
quotations_set_approval
Approves one or more quotations, or returns them to draft. Only an approved quotation prints with the organization stamp and the linked sales person's signature, when that person has one saved; approval does not depend on a signature existing.
Changes data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | integer[] | Yes | The quotation IDs, from 1 to 200. |
approved | boolean | Yes | true approves the quotations and false returns them to draft. |
{
"ids": [
311,
312,
313
],
"approved": true
}Returns: updated, the IDs that changed, and failed, the IDs not found in your organization, each with the error not_found.
- Any content edit, whether through
quotations_updateor a regeneration from the tender, returns the quotation to draft, so approve it again afterwards. - In the app, the approve button is limited to owners and admins. This tool is open to every role that may edit quotations, as the roles line above shows.
quotations_defaults_get
Reads the values a new quotation starts from in your organization, and the contact details it would take if you name no person. Use it before quotations_create to know what prints when you leave fields out.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
No parameters.
Returns: validityDays and deliveryDays (90 when your organization has set none), paymentTerms (or null), salesperson with the full_name, phone and email of the member who connected, and company: the letterhead a new quotation prints, with the legal names, registration and VAT numbers, address and contact details, and short-lived links to the logo and stamp.
Quotations on a tender
Tools that work on the quotations of one tender: listing them, generating them from its items and downloading their files.
tender_quotations_list
Lists all of your organization's quotations on one tender, newest first, with each one's number and totals.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | Yes | The tender's ID. |
Returns: A quotations list, each with id, quote_number, rfq_no, currency, total_subtotal, total_vat, total_grand and created_at.
- This list does not show the approval state; use
quotations_listwithtender_idfor it.
tender_quotations_generate
Generates quotations on a tender from the lines you send: per_item gives every line its own quotation and combined puts all the lines in one. Sending again is safe: lines an existing quotation already covers are refreshed in place instead of duplicated.
Changes data · scope quotations.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | Yes | The tender's ID, a tender your organization can see. |
mode | string | No | per_item for one quotation per line, the default, or combined for one quotation carrying all the lines. |
rfq_no | string | No | The buyer's own RFQ number, up to 100 characters. |
quote_validity_days | integer | No | How many days the offer stays valid, 1 to 3650. When left out, your organization's default applies (see quotations_defaults_get). |
delivery_period_days | integer | No | The delivery period in days, 1 to 3650. When left out, your organization's default applies. |
payment_terms | string | No | The payment terms printed on the quotation, up to 500 characters: one of the presets advance_100, advance_50_balance_on_delivery, on_delivery, net_30, net_60 or net_90 (printed in Arabic and English), or free text printed as typed. When left out, your organization's default terms apply; null prints none. |
currency | string | No | The currency code, 1 to 8 characters. Defaults to SAR. |
notes | string | No | Notes printed on the quotation, up to 2,000 characters. When left out, the standard closing line "Looking forward for your business" prints. |
sales_person_id | integer | No | The ID of a person on your sales roster, from sales_persons_list. Their details fill any salesperson_* field you leave out, and the quotations list can filter by them. An ID that is not on your roster answers sales_person_not_found. |
salesperson_name | string | No | The name printed as the contact person, up to 200 characters. When left out, the picked sales person's name is used, else the name of the member who connected. null prints none. |
salesperson_phone | string | No | The phone printed for the contact person, up to 50 characters. When left out, the picked sales person's phone is used, else the connected member's. null prints none. |
salesperson_email | string | No | The email printed for the contact person, a valid address up to 320 characters. When left out, the picked sales person's email is used, else the connected member's. null prints none. |
items | object[] | Yes | The lines to quote, from 1 to 500. |
items[].tender_item_id | integer | No | The ID of the tender item this line covers, from the tender's items in tenders_get. Used only on a quotation linked to a tender. |
items[].product_id | integer | No | The ID of the catalog product behind this line, from products_list. A linked product supplies the catalog number when you leave it blank and the VAT rate when you leave it out, and later feeds the product document with its catalog and certificates. |
items[].manufacturer_id | integer | No | The ID of the manufacturer this line is quoted under, from manufacturers_list; it must be one of your organization's. When left out, the product's default manufacturer is used, and null keeps it automatic, so the manufacturer is chosen when documents are generated. It decides the manufacturer name, the authorization letter and the agency type in the product document. |
items[].description | string | Yes | The line's description as printed, 1 to 4,000 characters. |
items[].stock_no | string | No | The stock or NUPCO material number, up to 100 characters. |
items[].catalog_no | string | No | The catalog number, up to 100 characters. |
items[].unit | string | No | The unit of measure, up to 50 characters. |
items[].quantity | number | Yes | The quantity, zero or more. |
items[].unit_price | number | Yes | The unit price in the quotation's currency, zero or more. |
items[].vat_rate | number | No | The VAT percentage, 0 to 100. When left out it is 0 for a line whose linked product is medical and 15 for everything else, including a line with no product. |
{
"tender_id": 48213,
"mode": "per_item",
"payment_terms": "net_30",
"sales_person_id": 6,
"items": [
{
"tender_item_id": 902114,
"product_id": 3381,
"description": "Disposable syringe 5 ml, Luer lock, sterile",
"unit": "Box",
"quantity": 200,
"unit_price": 24.5
},
{
"tender_item_id": 902115,
"product_id": 3390,
"description": "Surgical gloves, nitrile, size M",
"unit": "Box",
"quantity": 500,
"unit_price": 31
}
]
}Returns: A quotations list, each with quotation_id, quote_number and updated: true for a quotation refreshed in place and false for a new one.
- In
per_itemmode the existing quotation for the same tender item is refreshed under the same number; incombinedmode the one covering exactly the same set of tender items is. A quotation is refreshed only when your lines account for every line it carries; otherwise your lines become new quotations and the existing one is left alone. Anything with no existing quotation is created. - Give every line its
tender_item_id: a line without one cannot be matched to an earlier quotation, so every run creates a new one for it. - The header fields apply to new quotations only; a refreshed quotation keeps its header. Every refreshed quotation returns to draft and needs approving again.
- Only new quotations count against the monthly quota (50 on Starter, unlimited on Pro). The limit is checked before generation starts, so a refusal with
plan_limit_reachedcreates nothing. A failure part-way through stops the run, and the quotations made before it stay.
tender_quotation_pdf_bundle
Renders a PDF for every quotation on a tender and returns them in one ZIP archive. It runs in a single request, so for a tender with many quotations use quotation_packages_bundle with pdf_only, which builds the file in the background.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
tender_id | integer | Yes | The tender's ID. |
Returns: A ZIP file with one PDF per quotation: filename, content_type, size_bytes and base64.
- A tender with none of your organization's quotations answers 404 with
No quotations to download. - Each PDF prints the stamp and the signature if its quotation is approved, and prints without them if it is a draft.
Quotation files
The main file is the printed quotation. The product document is extra files per line that go with it: a cover letter with the catalog, the catalog alone, the SFDA certificate or a generated compliance letter, the manufacturer's authorization letter, and a plain-text file of the fields a buyer portal asks for. A file with no source in your data is left out of the archive without an error. It is explained in Quotations.
quotation_pdf
Renders one quotation as a PDF and returns it. It is the printed document itself: it carries the organization stamp and the sales person's signature when the quotation is approved, and a draft prints without a stamp and says it is not valid.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
Returns: A PDF file: filename, content_type, size_bytes and base64.
- The file is named after the quote number, except a single-line quotation on a NUPCO tender, which is named by the tender reference, the quote number and the item serial.
- A quotation that is not your organization's answers 404.
quotation_package
Returns everything the platform produces for one quotation in a single ZIP archive: the quotation's PDF and, for each line, the product document's files. The document's fields are the automatically filled values, with no edits; to change a field, use quotation_product_document.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
Returns: A ZIP file: filename, content_type, size_bytes and base64.
- Each line gets up to five files: the cover letter with the catalog, the catalog alone, the SFDA certificate or the generated compliance letter, the authorization letter, and the text file. Files with no source are left out: a line with no stored catalog gets a cover letter without catalog pages, and a line whose manufacturer has no stored authorization letter gets none.
- Files are named with the quote number and the line's position in the quotation, so they stay distinguishable outside the archive.
quotation_packages_bundle
Merges the packages of several quotations into flat ZIP archives, each quotation's PDF and product-document files side by side. It is the "download all files" of the quotations list. It is built in the background: the tool starts it and waits up to wait_seconds, then returns the file if it is done, or the job if it is not. It changes none of your data, and the finished file is kept for 24 hours.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | integer[] | Yes | The IDs of the quotations to download, from 1 to 100. |
pdf_only | boolean | No | true includes only each quotation's PDF, without the product-document files. It is the list's "Download quotation PDFs". |
report | boolean | No | true returns the job (its status, report and parts) instead of the file itself. |
wait_seconds | integer | No | How many seconds the tool waits for the bundle before answering with the job, 0 to 120. Defaults to 40, which usually covers a few quotations. |
{
"ids": [
311,
312,
313
],
"pdf_only": true,
"wait_seconds": 60
}Returns: When the archive is finished as a single part within the wait, the file: filename, content_type, size_bytes and base64, with report. Otherwise the job: job_id, status (queued, running, succeeded, failed or cancelled), progress, parts, report on success, and next_step saying what to do next.
- A large bundle comes in several ZIP parts, each quotation whole in one part, so extract every part into one folder. Fetch the parts with generation_jobs_part and follow the job with generation_jobs_get.
- One bundle builds per member at a time. If you already have one building, the tool answers with that job and
already_running(it may be for another selection); to start over, cancel it with generation_jobs_cancel. - A finished job's report lists every quotation in
includedwith its part, and every one that produced nothing infailedby quote number with a reason:not_found,render_failedortoo_large(its own files exceed one archive). No quotation is dropped silently. - If none of the IDs is a quotation of your organization, the tool answers 404. For a NUPCO upload set, prefer tenders_nupco_attachments_fill, whose packet is self-contained.
- This tool is not available to the viewer role.
quotation_product_document_prefill
Shows, for each line of a quotation, the values the product document will be filled with automatically and whether a catalog is stored for it. Use it to see what will print, or to build fields before calling quotation_product_document.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing, viewer · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
Returns: quote_number and lines, each with item_id, label, fields (the values by key), has_catalog and catalog_name.
- A quotation that is not your organization's answers 404.
quotation_product_document
Generates a quotation's product document as a ZIP of separate files: for each line the cover letter with the catalog, the catalog alone, the SFDA certificate or the generated compliance letter, the manufacturer's authorization letter, and a plain-text file of the buyer-portal registration fields. It covers every line or just one, and takes edits to the values per line.
Read-only · scope quotations.read · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The quotation's ID, from quotations_list. |
mode | string | Yes | combined covers every line of the quotation; single covers one line, named by item_id. |
item_id | integer | No | The ID of a line of this quotation, from quotations_get. Required with single, ignored with combined. |
fields | object | No | Edits to the filled values. Each key is a line's ID in the quotation (a numeric string such as "4807") and its value is an object of field names to text of up to 2,000 characters. What you do not send stays as filled, and an empty value clears that row. A misspelled field key is ignored without an error, so take the keys from quotation_product_document_prefill. |
{
"id": 311,
"mode": "single",
"item_id": 4807,
"fields": {
"4807": {
"product_title": "Disposable syringe 5 ml",
"min_order_qty": "1000"
}
}
}Returns: A ZIP file: filename, content_type, size_bytes and base64. File names start with the quote number and the line number, then cover, catalog, sfda, authorization or info.
- The cover page fields are
serial,nupco,company_name,manufacturer_name,product_title,description,catalogue,mdmaandlisting. The text file fields aremdma_number,mdma_expiry,min_order_qty,shelf_life_years,authorization_validity,volume,volume_unit,package_type,package_size,packing_uom,weight,weight_unit,unit_price,foc_percent,vat,net_price,additional_infoandagency_type. - A catalog cannot be uploaded through this tool: the stored catalogs of the lines' products are included automatically. A line with no stored catalog gets its cover letter alone, and a missing authorization letter or certificate is left out of the archive without an error.
- A product recorded as under registration gets the generated compliance letter instead of any stored certificate. A line that is not in the quotation, in
singlemode, answers 404, andsinglewithoutitem_idanswers 400. This tool is not available to the viewer role.
Sales people
The roster belongs to the whole organization: what a colleague adds, you see. An entry is not a login, only a name, an email and a phone that picking the person fills into a quotation's printed contact block.
sales_persons_list
Lists the people on your organization's sales roster, newest first. Deactivated people are hidden unless you ask for them, and the tool can also list team members not yet on the roster.
Read-only · scope sales_persons.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
include_inactive | boolean | No | true also lists deactivated people. |
include_team | boolean | No | true adds team_members: organization members not yet on the roster, ready to add with sales_persons_create and the member's user_id. |
page | integer | No | Page number, starting at 1. |
page_size | integer | No | Results per page: 20 by default, 100 at most. |
Returns: A page of people in data with total, page, page_size and total_pages. Each carries id, name, email, phone, user_id (for one linked to a team member) and is_active. With include_team, team_members is added, each member with user_id, name, email and phone.
team_memberscomes back only for the roles that may edit quotations (owner, admin, sales and purchasing); for other roles the request is ignored without an error, because the list exposes members' contact details.
sales_persons_get
Reads one person from your organization's roster: their name, email, phone and state, and whether they are linked to a team member. Use it to check what picking them will fill into a quotation.
Read-only · scope sales_persons.read · all roles · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The person's ID, from sales_persons_list. |
Returns: The sales_person, whose ID is what quotations carry in sales_person_id.
- An ID that is not on your organization's roster answers 404.
sales_persons_create
Adds a person to your organization's sales roster: either by name, email and phone, or by a team member's ID, in which case their details come from their profile and the entry stays linked to them. No login is created either way.
Changes data · scope sales_persons.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | The full name as it prints, 1 to 200 characters. Required unless you send user_id. |
email | string | No | The email printed on quotations, a valid address up to 320 characters. |
phone | string | No | The phone printed on quotations, up to 50 characters. |
user_id | string | No | A team member's ID (a UUID) from team_members in sales_persons_list. Their profile fills whichever of name, email and phone you do not send, and the entry updates by itself whenever the member edits their profile. |
{
"name": "Omar Al-Qahtani",
"email": "omar@acme-medical.example",
"phone": "+966501234567"
}Returns: The new sales_person.
- Each member has one roster entry: adding one again answers 409
already_on_roster. Auser_idthat is not a member of your organization answers 400not_a_team_member, and sending neithernamenoruser_idanswers 400. - A member's saved signature prints on an approved quotation only when the picked sales person is linked to that member.
sales_persons_update
Changes a person's name, email or phone, or deactivates and reactivates them. Only what you send changes. It never rewrites the quotations that already picked the person, since each quotation keeps its own copy of the contact details.
Changes data · scope sales_persons.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The person's ID, from sales_persons_list. |
name | string | No | The full name, 1 to 200 characters. |
email | string | No | The email, a valid address up to 320 characters. Send null to clear it. |
phone | string | No | The phone, up to 50 characters. Send null to clear it. |
is_active | boolean | No | false hides the person from pickers and from the roster's default list, and true brings them back. |
{
"id": 6,
"phone": "+966551234567",
"is_active": true
}Returns: The sales_person after the change.
- A person linked to a team member is refreshed from the member's profile whenever it changes, so a later profile edit may replace what you set here.
- Send at least one field, and an ID that is not on your roster answers 404.
sales_persons_delete
Deletes a person from the roster, or deactivates them instead when any quotation references them; the reply says which happened. The printed contact details in quotations stay either way.
Deletes or overwrites data · scope sales_persons.write · roles: owner, admin, sales, purchasing · Pro and Enterprise plans
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | The person's ID, from sales_persons_list. |
Returns: ok: true and deactivated: true when the person was deactivated instead of deleted, false when deleted.
- A real deletion cannot be undone, while a deactivation is reversed with
sales_persons_updateandis_activeset totrue. An ID that is not on your roster answers 404.