Skip to content

proposals — full tool reference

Editais/RFPs knowledge and assembly: company registry, document vault, answer library, opportunities, submissions.

Endpointhttps://proposals.mcp.devfellowship.com/mcp
Packagepackages/dfl-mcp-proposals
Tools21
ToolDescription
list_companiesList the companies that can apply to an opportunity — own entities (Revera/Itera/devfellowship) and external partners (e.g. B42). Filter by relationship (own|partner) or search legal_name / trade_name / cnpj.
get_companyGet one company by id, optionally with its fiscal-year history (revenue/headcount) and contacts (accountant/legal/admin/partner). Note: the underlying vault documents are admin-gated and NOT returned here — use list_expiring_documents / upload_company_doc for the vault.
upsert_companyCreate a new company, or update an existing one when id is given. relationship (own|partner) is required when creating. Partners (e.g. B42) keep business_unit_id NULL; own entities may point at the canonical public.business_units roster (soft reference, app-enforced).
upload_company_docUpload a SMALL document into the company vault by sending its bytes inline: base64 file_content is POSTed to the upload-file edge function as PRIVATE (public.media row, folder proposals/<company_id>/) and a proposals.company_documents metadata row is recorded pointing at it. The read path is always share.devfellowship.com/<media_id> or an MCP link — never a raw S3 URL. ⚠️ The base64 body is capped by the server bodyLimit (~10 MB body ≈ ~7.5 MB file); for LARGER files, upload to the private bucket first (upload-file edge function, visibility=private) and use link_company_doc with the returned media_id/URL — no bytes travel through the MCP body. VAULT tables are admin-gated (iam.is_global_admin) — the caller must be a global admin.
link_company_docThe definitive large-file path: record a proposals.company_documents vault row that points at a file ALREADY uploaded to the private bucket — NO file bytes travel through the MCP body, so there is no size limit. Upload the file first via the upload-file edge function (visibility=private) — that returns a media_id + a stable https://media.devfellowship.com/&lt;id> link — then pass that reference here. media_ref accepts a bare media UUID, a media.devfellowship.com/<id> or share.devfellowship.com/<id> URL, or a private/<id>-<name> storage path. Optionally pass supersedes to replace an existing vault row (e.g. swap a compressed stopgap for the byte-exact original): the old row is marked status=superseded + superseded_by=<new id>. The read path is always share.devfellowship.com/<media_id> — never a raw S3 URL. VAULT tables are admin-gated (iam.is_global_admin) — the caller must be a global admin.
list_expiring_documentsThe certificate-renewal radar: vault documents whose valid_until falls within within_days from today (already-expired included). Order by soonest expiry. VAULT is admin-gated (iam.is_global_admin) — a non-admin caller gets an empty list, not an error.
search_answersSearch the reusable answer library by free text (question_canonical / short_answer / long_answer_md) and/or facet tags. This is the reuse entry point — find an approved answer, then reference it from a submission section (never copy-paste). Returns each answer with its tags. Every row also reports its BODY TYPE: answer_kind is “markdown” or “sheet”. A “sheet” answer keeps its content in the work.sheets row named by sheet_id, NOT in long_answer_md — open it on the engineering MCP with get_sheet, and use copy_sheet when a submission needs its own fillable copy. Free-text search reads the markdown columns only, so find a budget template by its question_canonical or its tags.
create_answerAdd a reusable answer to the library. company_id NULL = ecosystem-shared (DFL narrative); set = company-specific fact. New answers default to status=“draft”. Optionally attach facet tags and link a translation (translation_of) to pair PT/EN. SHEETS: an answer may BE a spreadsheet instead of Markdown. Create the sheet first on the engineering MCP (create_sheet, or copy_sheet to fill a template for one submission), then attach it here by passing its id as sheet_id. The kind field is inferred from sheet_id, so you rarely set it by hand; pass sheet_id: null to detach the sheet and go back to Markdown.
update_answerEdit an answer in the library. Optionally records an append-only revision snapshot (record_revision) and/or fully replaces the answer’s facet tags (tags). Use set_answer_status for status transitions. SHEETS: an answer may BE a spreadsheet instead of Markdown. Create the sheet first on the engineering MCP (create_sheet, or copy_sheet to fill a template for one submission), then attach it here by passing its id as sheet_id. The kind field is inferred from sheet_id, so you rarely set it by hand; pass sheet_id: null to detach the sheet and go back to Markdown. A recorded revision snapshots sheet_id and answer_kind together with long_answer_md, so the history of a sheet answer stays complete.
set_answer_statusTransition an answer through its governance lifecycle: draft → approved → stale → retired. Approving stamps last_reviewed_at=now. This is the governance lever that keeps the library from rotting into a wiki.
list_stale_answersThe library review queue: answers explicitly marked status=“stale” PLUS answers whose expires_at falls within within_days from now (already-expired included). Retired answers are excluded. Order by soonest expiry. Each row reports answer_kind and sheet_id, so a spreadsheet answer up for review is visible as one: its content lives in work.sheets, not in long_answer_md.
create_opportunityRegister an edital/RFP/public tender/incentive program/award as a structured object (deadlines, value, status). Optionally attach a funder by id, or by name (a funder row is created on the fly). Optionally point source_media_id at the notice PDF already in the vault.
add_requirementsBulk-add the extracted checklist for an opportunity: document/eligibility/content/form/budget requirements, each with provenance (source_excerpt + source_page) back to the notice PDF. document requirements should set required_document_type so eligibility_check can match them against a company vault.
eligibility_checkCross a company against an opportunity’s requirements and return a per-requirement verdict + gap list. document requirements are auto-checked against the company vault (present + current + not-expired); eligibility criteria like {“min_revenue”,“min_years”,“min_headcount”} are auto-checked against fiscal years / incorporation date; content/form/budget requirements are flagged manual_review. NOTE: the vault is admin-gated — a non-admin caller sees no documents, so every document requirement reports “missing”.
list_opportunitiesList editais/RFPs/public tenders/incentive programs/awards already registered in the pipeline. Filter by status, funder_id, or kind. This is the discovery entry point — check here before create_opportunity to avoid registering a duplicate. Each row includes its resolved funder (id/name/kind).
search_opportunitiesText search over opportunities by title, notice_number (número do edital), or funder name — the dedup entry point before create_opportunity (find it first, don’t create it blind). Combine with status/kind/funder_id filters. Each row includes its resolved funder.
create_submissionOpen a submission = one company applying to one opportunity (the multi-company anchor). Seeds the applying company as “lead” in submission_companies. Pass consortium to add partner companies (e.g. B42 as consortium_member). plan_slug ties it to the plans-app authoring workspace.
upsert_sectionCreate or update a section of a submission. answer_id is the reuse/provenance link into the answer library (ADR-3: reference, never copy-paste); content_md is the opportunity-tailored final text ADAPTED from that answer. Pass id to update an existing section, omit to create. SHEETS: a section may BE a spreadsheet instead of Markdown — a budget table, for instance. Fill a template for one submission on the engineering MCP with copy_sheet (or build a new one with create_sheet), then pass the new sheet id as sheet_id here. section_kind is inferred from sheet_id; pass sheet_id: null to detach the sheet and go back to Markdown. answer_id stays the provenance link either way.
attach_documentThe compliance join: record that a specific vault document (company_document_id) satisfies a submission — optionally tied to the exact requirement it fulfills (requirement_id). This is what builds the submission checklist. The team can see “cartão CNPJ: attached” here even without vault read access to open the file itself.
submission_checklistAssemble the full compliance + drafting state of a submission: every opportunity requirement with the documents attached to satisfy it (submission_documents), plus each submission section and its status. Readable at member tier (the checklist is team-visible even when the underlying vault files are admin-gated). Each section reports its BODY TYPE: section_kind is “markdown” or “sheet”, and a sheet section carries the work.sheets id in sheet_id — read or edit it on the engineering MCP (get_sheet / write_sheet_cells). sections_sheet counts them.
harvest_answersThe loop that compounds: promote reusable text written in a past submission back into the answer library. For each qualifying section, create a draft library answer (question_canonical = section title, long_answer_md = section content, source = provenance to this submission) and back-link the section to the new answer via answer_id. By default only sections NOT already linked to a library answer are harvested. SHEETS: a section whose body is a spreadsheet (section_kind=“sheet”) qualifies on its sheet_id alone, with or without content_md, and the promoted answer carries the SAME sheet_id with answer_kind=“sheet”. The sheet itself is never copied — the library answer and the submission section point at one work.sheets row, so editing it later shows in both.

List Companies

List the companies that can apply to an opportunity — own entities (Revera/Itera/devfellowship) and external partners (e.g. B42). Filter by relationship (own|partner) or search legal_name / trade_name / cnpj.

ParameterTypeRequiredDescription
relationshipenumnoFilter by relationship: “own” (DFL entity) or “partner” (external, e.g. B42) One of: own, partner.
searchstringnoCase-insensitive substring match on legal_name, trade_name, or cnpj
limitnumbernoMax rows (default 50, max 100)
offsetnumbernoRows to skip (pagination)

Get Company

Get one company by id, optionally with its fiscal-year history (revenue/headcount) and contacts (accountant/legal/admin/partner). Note: the underlying vault documents are admin-gated and NOT returned here — use list_expiring_documents / upload_company_doc for the vault.

ParameterTypeRequiredDescription
idstringyesThe UUID of the company
include_relatedbooleannoAlso return company_fiscal_years and company_contacts (default false)

Upsert Company

Create a new company, or update an existing one when id is given. relationship (own|partner) is required when creating. Partners (e.g. B42) keep business_unit_id NULL; own entities may point at the canonical public.business_units roster (soft reference, app-enforced).

ParameterTypeRequiredDescription
idstringnoUUID of an existing company to UPDATE. Omit to CREATE a new one.
relationshipenumnoown = DFL entity (Revera/Itera/devfellowship); partner = external. REQUIRED when creating. One of: own, partner.
business_unit_idstringnoCanonical public.business_units id for own entities; NULL for partners (soft reference, no FK).
cnpjstringnoBrazilian company tax id (unique across companies)
legal_namestringnoRazão social (legal name)
trade_namestringnoNome fantasia (trade name)
incorporated_atstringnoIncorporation date (YYYY-MM-DD)
legal_formstringnoNatureza jurídica (legal form, e.g. LTDA)
tax_regimestringnoRegime tributário (e.g. Simples Nacional, Lucro Presumido)
primary_cnaestringnoPrimary CNAE code
share_capitalnumbernoCapital social (numeric)
state_registrationstringnoInscrição estadual
municipal_registrationstringnoInscrição municipal
fiscal_addressobjectnoFiscal address as a JSON object
company_sizeenumnoPorte (legal size classification) One of: MEI, ME, EPP, other.
extraobjectnoCatch-all JSON for fields not yet promoted to columns

Upload Company Document (Vault)

Upload a SMALL document into the company vault by sending its bytes inline: base64 file_content is POSTed to the upload-file edge function as PRIVATE (public.media row, folder proposals/<company_id>/) and a proposals.company_documents metadata row is recorded pointing at it. The read path is always share.devfellowship.com/<media_id> or an MCP link — never a raw S3 URL. ⚠️ The base64 body is capped by the server bodyLimit (~10 MB body ≈ ~7.5 MB file); for LARGER files, upload to the private bucket first (upload-file edge function, visibility=private) and use link_company_doc with the returned media_id/URL — no bytes travel through the MCP body. VAULT tables are admin-gated (iam.is_global_admin) — the caller must be a global admin.

ParameterTypeRequiredDescription
company_idstringyesUUID of the company this document belongs to
file_contentstringyesBase64-encoded file content
file_namestringyesFile name with extension (e.g. “contrato_social.pdf”)
mime_typestringyesMIME type (e.g. “application/pdf”)
document_typestringyesFree-text type, e.g. contrato_social | cartao_cnpj | certidao_* | balanco | atestado
labelstringnoHuman label for the document
notesstringnoFreeform notes
issued_atstringnoIssue date (YYYY-MM-DD)
valid_untilstringnoValidity/expiry date (YYYY-MM-DD) — drives the certificate-renewal radar

Link Pre-Uploaded Company Document (Vault)

The definitive large-file path: record a proposals.company_documents vault row that points at a file ALREADY uploaded to the private bucket — NO file bytes travel through the MCP body, so there is no size limit. Upload the file first via the upload-file edge function (visibility=private) — that returns a media_id + a stable https://media.devfellowship.com/&lt;id> link — then pass that reference here. media_ref accepts a bare media UUID, a media.devfellowship.com/<id> or share.devfellowship.com/<id> URL, or a private/<id>-<name> storage path. Optionally pass supersedes to replace an existing vault row (e.g. swap a compressed stopgap for the byte-exact original): the old row is marked status=superseded + superseded_by=<new id>. The read path is always share.devfellowship.com/<media_id> — never a raw S3 URL. VAULT tables are admin-gated (iam.is_global_admin) — the caller must be a global admin.

ParameterTypeRequiredDescription
company_idstringyesUUID of the company this document belongs to
media_refstringyesReference to an ALREADY-uploaded PRIVATE file: a media UUID, a media.devfellowship.com/<id> or share.devfellowship.com/<id> URL, or a private/<id>-<name> storage path. The file must have been uploaded via the upload-file edge function with visibility=private first.
document_typestringyesFree-text type, e.g. contrato_social | cartao_cnpj | certidao_* | balanco | atestado
labelstringnoHuman label for the document
notesstringnoFreeform notes
issued_atstringnoIssue date (YYYY-MM-DD)
valid_untilstringnoValidity/expiry date (YYYY-MM-DD) — drives the certificate-renewal radar
supersedesstringnoUUID of an existing proposals.company_documents row this document REPLACES. When set, that row is marked status=superseded and superseded_by=<new row id> after the new row is inserted (e.g. replacing a compressed stopgap with the byte-exact original).

List Expiring Documents

The certificate-renewal radar: vault documents whose valid_until falls within within_days from today (already-expired included). Order by soonest expiry. VAULT is admin-gated (iam.is_global_admin) — a non-admin caller gets an empty list, not an error.

ParameterTypeRequiredDescription
within_daysnumbernoLook-ahead window in days (default 30). Documents expiring within this many days (or already expired) are returned.
company_idstringnoRestrict to one company
include_expiredbooleannoInclude already-expired documents (default true)

Search Answers (Library)

Search the reusable answer library by free text (question_canonical / short_answer / long_answer_md) and/or facet tags. This is the reuse entry point — find an approved answer, then reference it from a submission section (never copy-paste). Returns each answer with its tags. Every row also reports its BODY TYPE: answer_kind is “markdown” or “sheet”. A “sheet” answer keeps its content in the work.sheets row named by sheet_id, NOT in long_answer_md — open it on the engineering MCP with get_sheet, and use copy_sheet when a submission needs its own fillable copy. Free-text search reads the markdown columns only, so find a budget template by its question_canonical or its tags.

ParameterTypeRequiredDescription
querystringnoCase-insensitive substring across question_canonical, short_answer, long_answer_md
tagsstring[]noOnly answers carrying ALL of these facet tags
statusenumnoFilter by governance status One of: draft, approved, stale, retired.
languageenumnoFilter by language One of: pt, en.
company_idstringnoCompany-specific answers for this company id
shared_onlybooleannoOnly ecosystem-shared answers (company_id IS NULL)
limitnumbernoMax rows (default 50, max 100)
offsetnumbernoRows to skip (pagination)

Create Answer

Add a reusable answer to the library. company_id NULL = ecosystem-shared (DFL narrative); set = company-specific fact. New answers default to status=“draft”. Optionally attach facet tags and link a translation (translation_of) to pair PT/EN. SHEETS: an answer may BE a spreadsheet instead of Markdown. Create the sheet first on the engineering MCP (create_sheet, or copy_sheet to fill a template for one submission), then attach it here by passing its id as sheet_id. The kind field is inferred from sheet_id, so you rarely set it by hand; pass sheet_id: null to detach the sheet and go back to Markdown.

ParameterTypeRequiredDescription
question_canonicalstringyesThe canonical question/prompt this answer responds to
short_answerstringnoOne/two-line summary
long_answer_mdstringnoFull answer in Markdown
languageenumnoLanguage (default pt) One of: pt, en.
company_idstringnoCompany id, or NULL/omit for ecosystem-shared
translation_ofstringnoUUID of the source-language answer this one translates
statusenumnoGovernance status (default draft) One of: draft, approved, stale, retired.
expires_atstringnoWhen this answer should be re-reviewed (ISO timestamp)
tagsstring[]noFacet tags (e.g. institutional, impact, safeguarding)
sourceobjectnoProvenance JSON (plan slug, submission id, vault doc)
sheet_idstringnoUUID of a work.sheets spreadsheet that IS this answer (e.g. the “Orçamento detalhado” budget). Create it on the engineering MCP with create_sheet or copy_sheet first. Passing it sets answer_kind=“sheet” on its own.
answer_kindenumnoWhich body this answer carries (default markdown, inferred as “sheet” when sheet_id is given). “sheet” without a sheet_id is refused here, before the database CHECK. One of: markdown, sheet.

Update Answer

Edit an answer in the library. Optionally records an append-only revision snapshot (record_revision) and/or fully replaces the answer’s facet tags (tags). Use set_answer_status for status transitions. SHEETS: an answer may BE a spreadsheet instead of Markdown. Create the sheet first on the engineering MCP (create_sheet, or copy_sheet to fill a template for one submission), then attach it here by passing its id as sheet_id. The kind field is inferred from sheet_id, so you rarely set it by hand; pass sheet_id: null to detach the sheet and go back to Markdown. A recorded revision snapshots sheet_id and answer_kind together with long_answer_md, so the history of a sheet answer stays complete.

ParameterTypeRequiredDescription
idstringyesUUID of the answer to update
question_canonicalstringnoUpdate the canonical question
short_answerstringnoUpdate the short answer
long_answer_mdstringnoUpdate the long answer (Markdown)
languageenumnoUpdate language One of: pt, en.
company_idstringnoReassign company (NULL = ecosystem-shared)
translation_ofstringnoUpdate the translation pairing
expires_atstringnoUpdate the re-review date
sourceobjectnoReplace the provenance JSON
tagsstring[]noIf provided, FULLY REPLACES the answer’s tags with this set
record_revisionbooleannoIf true, append a row to answer_revisions capturing the new long_answer_md PLUS sheet_id and answer_kind (default false)
sheet_idstringnoAttach a work.sheets spreadsheet as this answer’s body (create it on the engineering MCP with create_sheet or copy_sheet first), or pass null to detach it and go back to Markdown.
answer_kindenumnoWhich body this answer carries. Inferred from sheet_id, so you rarely set it: “sheet” needs a sheet_id in this call or already on the row, and “markdown” alongside a new sheet_id is refused. One of: markdown, sheet.

Set Answer Status

Transition an answer through its governance lifecycle: draft → approved → stale → retired. Approving stamps last_reviewed_at=now. This is the governance lever that keeps the library from rotting into a wiki.

ParameterTypeRequiredDescription
idstringyesUUID of the answer
statusenumyesNew governance status One of: draft, approved, stale, retired.
expires_atstringnoOptionally (re)set the re-review date (ISO timestamp; null clears it)

List Stale Answers

The library review queue: answers explicitly marked status=“stale” PLUS answers whose expires_at falls within within_days from now (already-expired included). Retired answers are excluded. Order by soonest expiry. Each row reports answer_kind and sheet_id, so a spreadsheet answer up for review is visible as one: its content lives in work.sheets, not in long_answer_md.

ParameterTypeRequiredDescription
within_daysnumbernoAlso include answers expiring within this many days (default 0 = only already-expired + status=stale)
company_idstringnoRestrict to one company
limitnumbernoMax rows (default 100, max 200)

Create Opportunity

Register an edital/RFP/public tender/incentive program/award as a structured object (deadlines, value, status). Optionally attach a funder by id, or by name (a funder row is created on the fly). Optionally point source_media_id at the notice PDF already in the vault.

ParameterTypeRequiredDescription
kindenumyesgrant_notice=edital, rfp, public_tender=licitação, incentive_program=incentivo, award=prêmio One of: grant_notice, rfp, public_tender, incentive_program, award.
titlestringyesHuman title of the opportunity
notice_numberstringnoNúmero do edital / notice number
urlstringnoPublic URL of the opportunity notice
funder_idstringnoUUID of an existing funder
funder_namestringnoIf no funder_id, create a funder with this name and link it
funder_kindenumnoKind of the on-the-fly funder (only used with funder_name) One of: federal, state, municipal, multilateral, foundation, corporate.
source_media_idstringnopublic.media id of the notice PDF in the vault
published_atstringnoPublication timestamp (ISO)
questions_deadlinestringnoClarification-questions deadline (ISO)
submission_deadlinestringnoSubmission deadline (ISO)
total_valuenumbernoTotal value of the opportunity
currencystringnoCurrency code (default BRL)
statusenumnoPipeline status (default scouted) One of: scouted, analyzing, go, no_go, preparing, submitted, won, lost, cancelled.
go_no_go_notesstringnoGo/No-Go decision notes

Add Opportunity Requirements

Bulk-add the extracted checklist for an opportunity: document/eligibility/content/form/budget requirements, each with provenance (source_excerpt + source_page) back to the notice PDF. document requirements should set required_document_type so eligibility_check can match them against a company vault.

ParameterTypeRequiredDescription
opportunity_idstringyesUUID of the opportunity
requirementsobject[]yesOne or more requirements to insert

Eligibility Check

Cross a company against an opportunity’s requirements and return a per-requirement verdict + gap list. document requirements are auto-checked against the company vault (present + current + not-expired); eligibility criteria like {“min_revenue”,“min_years”,“min_headcount”} are auto-checked against fiscal years / incorporation date; content/form/budget requirements are flagged manual_review. NOTE: the vault is admin-gated — a non-admin caller sees no documents, so every document requirement reports “missing”.

ParameterTypeRequiredDescription
opportunity_idstringyesUUID of the opportunity
company_idstringyesUUID of the applying company

List Opportunities

List editais/RFPs/public tenders/incentive programs/awards already registered in the pipeline. Filter by status, funder_id, or kind. This is the discovery entry point — check here before create_opportunity to avoid registering a duplicate. Each row includes its resolved funder (id/name/kind).

ParameterTypeRequiredDescription
statusenumnoFilter by pipeline status One of: scouted, analyzing, go, no_go, preparing, submitted, won, lost, cancelled.
funder_idstringnoFilter by funder UUID
kindenumnoFilter by opportunity kind (grant_notice=edital, public_tender=licitação, incentive_program=incentivo, award=prêmio) One of: grant_notice, rfp, public_tender, incentive_program, award.
limitnumbernoMax rows (default 50, max 100)
offsetnumbernoRows to skip (pagination)

Search Opportunities

Text search over opportunities by title, notice_number (número do edital), or funder name — the dedup entry point before create_opportunity (find it first, don’t create it blind). Combine with status/kind/funder_id filters. Each row includes its resolved funder.

ParameterTypeRequiredDescription
querystringnoCase-insensitive substring across title, notice_number, and funder name
statusenumnoFilter by pipeline status One of: scouted, analyzing, go, no_go, preparing, submitted, won, lost, cancelled.
kindenumnoFilter by opportunity kind One of: grant_notice, rfp, public_tender, incentive_program, award.
funder_idstringnoFilter by funder UUID
limitnumbernoMax rows (default 50, max 100)
offsetnumbernoRows to skip (pagination)

Create Submission

Open a submission = one company applying to one opportunity (the multi-company anchor). Seeds the applying company as “lead” in submission_companies. Pass consortium to add partner companies (e.g. B42 as consortium_member). plan_slug ties it to the plans-app authoring workspace.

ParameterTypeRequiredDescription
opportunity_idstringyesUUID of the opportunity
company_idstringyesUUID of the primary applying company (recorded as lead)
statusenumnoPipeline status (default draft) One of: draft, internal_review, submitted, clarifications, won, lost, withdrawn.
plan_slugstringnoplans-app plan slug used as the authoring workspace
consortiumobject[]noAdditional consortium companies beyond the lead

Upsert Submission Section

Create or update a section of a submission. answer_id is the reuse/provenance link into the answer library (ADR-3: reference, never copy-paste); content_md is the opportunity-tailored final text ADAPTED from that answer. Pass id to update an existing section, omit to create. SHEETS: a section may BE a spreadsheet instead of Markdown — a budget table, for instance. Fill a template for one submission on the engineering MCP with copy_sheet (or build a new one with create_sheet), then pass the new sheet id as sheet_id here. section_kind is inferred from sheet_id; pass sheet_id: null to detach the sheet and go back to Markdown. answer_id stays the provenance link either way.

ParameterTypeRequiredDescription
submission_idstringyesUUID of the submission
idstringnoUUID of an existing section to UPDATE (omit to CREATE)
titlestringnoSection title
sort_ordernumbernoOrdering within the submission
answer_idstringnoLibrary answer this section reuses (provenance FK; also gives usage-tracking for free)
content_mdstringnoThe final, opportunity-adapted text (Markdown)
statusenumnoSection drafting status (default todo on create) One of: todo, drafted, reviewed, final.
sheet_idstringnoUUID of the work.sheets spreadsheet that IS this section (typically a copy_sheet of the opportunity budget template). Pass null to detach it.
section_kindenumnoWhich body this section carries (default markdown, inferred as “sheet” when sheet_id is given). “sheet” without a sheet_id is refused here, before the database CHECK. One of: markdown, sheet.

Attach Document to Submission

The compliance join: record that a specific vault document (company_document_id) satisfies a submission — optionally tied to the exact requirement it fulfills (requirement_id). This is what builds the submission checklist. The team can see “cartão CNPJ: attached” here even without vault read access to open the file itself.

ParameterTypeRequiredDescription
submission_idstringyesUUID of the submission
company_document_idstringyesUUID of the vault document (proposals.company_documents) that satisfies the requirement
requirement_idstringnoUUID of the opportunity_requirement this document fulfills (optional)
statusenumnoAttachment status (default pending) One of: pending, attached, needs_renewal.

Submission Checklist

Assemble the full compliance + drafting state of a submission: every opportunity requirement with the documents attached to satisfy it (submission_documents), plus each submission section and its status. Readable at member tier (the checklist is team-visible even when the underlying vault files are admin-gated). Each section reports its BODY TYPE: section_kind is “markdown” or “sheet”, and a sheet section carries the work.sheets id in sheet_id — read or edit it on the engineering MCP (get_sheet / write_sheet_cells). sections_sheet counts them.

ParameterTypeRequiredDescription
submission_idstringyesUUID of the submission

Harvest Answers from Submission

The loop that compounds: promote reusable text written in a past submission back into the answer library. For each qualifying section, create a draft library answer (question_canonical = section title, long_answer_md = section content, source = provenance to this submission) and back-link the section to the new answer via answer_id. By default only sections NOT already linked to a library answer are harvested. SHEETS: a section whose body is a spreadsheet (section_kind=“sheet”) qualifies on its sheet_id alone, with or without content_md, and the promoted answer carries the SAME sheet_id with answer_kind=“sheet”. The sheet itself is never copied — the library answer and the submission section point at one work.sheets row, so editing it later shows in both.

ParameterTypeRequiredDescription
submission_idstringyesUUID of the submission to harvest from
section_idsstring[]noRestrict to these section UUIDs (default: all qualifying sections)
only_unlinkedbooleannoOnly harvest sections with no answer_id yet (default true)
company_scopedbooleannoIf true, set the new answers’ company_id to the submission’s company (default false = ecosystem-shared)