Facebook Lead Ads: choose the route, build the form and prove the lead arrived
Choose the capture route, specify the form and CRM handoff, then prove a bounded test record arrived with the expected values and owner.
Facebook Lead Ads can collect a response inside a Meta Instant Form or send the click to a form on your website. Choose the route from what the prospect needs to do after the click.
Use an Instant Form when the next step is simple: request a guide, register interest or answer a few qualification questions. Use your website when the prospect needs to book a time, build a quote, review more product detail or share sensitive information. Meta also documents a website route with an Instant Form add-on, although availability can vary by account.
The route is only the first decision. Before launch, specify the offer, ad, form, privacy treatment, CRM mapping, owner and test evidence together. A submitted form is a submission. It becomes delivered, accepted, contacted, qualified or booked only when the matching event is observed.
Instant Form, website or hybrid?
| Route | Choose it when | What has to be proved before launch |
|---|---|---|
| Instant Form | The promised next step fits in a short native flow and the required fields can be collected responsibly inside Meta. | The form, privacy notice, completion action and Meta-to-CRM delivery all match the handoff. |
| Website | The prospect needs a booking flow, quote builder, detailed product explanation or another experience the native form cannot carry. | The page loads, the promise continues after the click, the form records attribution and the submitted record reaches its owner. |
| Website with Instant Form add-on | You deliberately want Meta to offer a second capture path and the option is available in the account. | Both routes have their own message, field, privacy and delivery checks. One working route does not prove the other. |
For a gated planning guide with name, work email, company size and one buying-stage question, start with an Instant Form. For a tailored assessment that needs scheduling, account data or a detailed explanation, start with the website.
Save the selected route, the reason and the promised next action in the handoff before opening Ads Manager. That prevents the form builder from making the decision for you.
Meta's current public guidance supports this route distinction. It does not predict which route will produce better leads for your campaign. Your offer, audience, follow-up and measurement still determine what happens next.
Define the offer before the form
Write one sentence that names the resource or action, who it is for and what happens after submission.
For the fictional example used throughout this guide:
Finance leaders at B2B software companies can request a 30-minute pipeline planning workbook. After submitting the form, they can open the workbook immediately and choose whether to request a planning call.
That sentence sets the contract for the ad and form:
- The ad must name the workbook and the intended reader.
- The form introduction must repeat the same resource and next action.
- Every question must help deliver the resource, qualify the request or route follow-up.
- The ending must provide the promised access and state what happens next.
If the offer sentence cannot be written without words such as “insights,” “growth” or “learn more,” the offer is not specific enough yet. Name the actual file, session, assessment or product action.
Keep the ad and form on the same promise
Use one short message contract. This is the completed fictional version:
| Part | Supplied wording or decision |
|---|---|
| Audience | Finance leaders at B2B software companies |
| Situation | The team needs a defensible pipeline target before the next planning cycle |
| Offer | 30-minute pipeline planning workbook |
| Ad opening | Set next quarter's pipeline target with the numbers your finance and revenue teams already have. |
| Headline | Build your pipeline target in 30 minutes |
| CTA | Download |
| Form introduction | Get the workbook and the inputs needed to set a first pipeline target. |
| Questions | Work email, company, employee range, planning stage |
| Ending | Open the workbook now. A planning call is optional. |
Read the row from top to bottom. The reader should not move from a planning workbook in the ad to a vague consultation in the form. The required commitment should stay the same until the optional next step is introduced clearly.
Five visible offer examples
These examples show how recognizable B2B advertisers make the offer visible in the creative, headline and button. The stored records for all five point to external websites. They do not reveal an Instant Form, hidden questions, consent wording, CRM delivery, lead quality or results.
HubSpot and TikTok: name the guide everywhere
The creative says “TikTok lead gen, made simple” and “Get the guide.” The archived headline is “TikTok Lead Gen Guide,” with a Download button. The image, headline and next action all name the same resource.
Borrow the consistency. A person who responds to “Get the guide” should see the guide named again in the form introduction and ending.
Open Meta Ad Library record 2564417600646172
Salesforce: make the research offer easy to scan
The creative names the “7th State of Service Report,” shows a service professional and uses a simple chart. The archived headline repeats the report title and the button says Download.
Borrow the hierarchy: research title, audience context and a direct action. The survey statistics in the ad are Salesforce's claims and are not treated here as independent evidence.
Open Meta Ad Library record 3706729896156811
HubSpot: make the asset type explicit
The image says “FREE REPORT,” “Ideal Customer Profile Report” and “Download now.” The archived headline, “Build a sharper ICP,” adds the desired outcome without hiding the deliverable.
Borrow the asset clarity. If the offer is a report, checklist or worksheet, say so before the click.
Open Meta Ad Library record 4006439549494086
Salesforce: show the subject and the deliverable
The creative combines a contact-center report mockup with service staff and the line “Less friction, more resolution.” The archived headline says “Get the new Contact Center Guide,” with a Download button.
Borrow the combination: show the subject, the deliverable and the action. Do not make the reader infer what arrives after submitting.
Open Meta Ad Library record 4032064863766233
monday.com: match commitment to the problem
The opening frame identifies monday.com, an HR and recruiting context, and the action “Build your HR agents.” The archived headline is “Recruit Smarter with AI Agents,” with a Sign up button.
This is a higher-commitment action than downloading a report. The ad names a specific role and problem, while the website has to carry the explanation that cannot fit in the short ad.
Open Meta Ad Library record 2484682258601889
Build the Instant Form in seven decisions
Meta's current Help documentation starts lead-campaign setup with the Leads objective, then asks for a conversion location and performance goal. Labels and account availability can change, so treat the screenshots below as orientation and check the current account before launch.
1. Confirm the destination and existing form
Start at the ad level with the correct Facebook Page, campaign and ad set visible. Confirm the conversion location chosen earlier. Then select an existing form or create the planned form.
Expected result: the handoff names the campaign, ad set, ad, Page, form and selected route. If any object is missing or the wrong Page is selected, stop and correct the object path before editing the form.
2. Choose the form type from the review needed
Meta's current Help documentation names three form types: More volume, Higher intent and Rich creative. Higher intent adds a review step before submission. Availability can vary.
Use More volume when the request is simple and the risk of accidental submission is low. Use Higher intent when the person should review the supplied information before sending. Use Rich creative only when the available presentation supports the offer better and the account exposes it.
Record the chosen type and reason. “Higher quality” is not a reason by itself. Name the review behavior you need.
3. Add the smallest contact set that supports the next action
Collect a field only when the campaign has a defined use for it.
For the fictional workbook example, collect:
- Work email, to deliver the workbook and match the record.
- Full name, to identify the person in follow-up.
- Company name, to route and deduplicate the account.
- Employee range, when the follow-up changes by company size.
- Planning stage, to distinguish active planning from general research.
Do not collect a phone number because “sales might want it.” Add it only when the promised follow-up includes a call or text, the consent treatment is approved and the field map has an owner.
4. Make each qualification question drive a decision
Meta's current Help lists Multiple choice, Short answer, Conditional and Appointment request among the question types, and says advertisers can add up to 15 custom questions. The available controls may differ by account.
Write the answer options, rule and owner action together:
| Question | Options | Decision it supports | Owner action |
|---|---|---|---|
| When will you set the next pipeline target? | This month; This quarter; Later; Researching | Separates an active planning window from general research. | Route This month and This quarter to the demand generation owner; send the workbook to every valid submission. |
| How many people work at the company? | 1–49; 50–249; 250–999; 1,000+ | Chooses the correct planning example and follow-up lane. | Store the answer in the CRM without turning it into a qualification result by itself. |
A qualification question should not ask for information nobody uses. If an answer does not change routing, follow-up or the resource delivered, remove the question.
5. Add privacy and consent before the ending
Enter the company's privacy-policy URL and the approved notice explaining why contact information is collected and how it will be used or shared. Meta says a privacy URL cannot point directly to a PDF, image or download.
The handoff needs:
- The exact privacy URL.
- The exact notice or custom disclaimer text.
- The person responsible for approval.
- The approval state and date.
- The destination field for any optional custom-disclaimer response.
Meta's developer documentation says optional custom-disclaimer responses are returned separately from the usual field_data. Give those responses their own field map and receipt check.
If the privacy URL, approval or owner is missing, the form remains on Hold. Do not turn a placeholder into an approval.
6. Write the ending as a real next step
The ending should deliver the promise or tell the person exactly what happens next. For the fictional workbook:
Your pipeline workbook is ready. Open it now to enter the four planning inputs. If you want help reviewing the result, request a planning call after you finish the worksheet.
Save the destination URL and inspect the real destination headline and action. A button label alone does not prove continuity.
7. Run the preflight before publication
Check the handoff from top to bottom:
- The Page, campaign, ad set, ad and form IDs identify the intended objects.
- The ad, form introduction and ending name the same offer.
- Every field and question has a defined use.
- Privacy and consent text has an owner and approval state.
- The ending performs the promised next action.
- Every collected value maps into a destination field.
- Deduplication, assignment, response target and failure owner are named.
- The transport test and source-origin test have separate states.
Map every value into the CRM
Lead retrieval moves a Meta submission to the business. Downstream feedback moves later CRM status back to Meta through Conversions API for CRM. These are separate directions and need separate evidence.
For retrieval, map the value, type and fallback before implementation:
| Source value | Destination field | Type | If missing or invalid |
|---|---|---|---|
| Meta lead ID | meta_lead_id | Text, unique | Hold the record for integration review. |
| Page ID | meta_page_id | Text | Hold if the source Page cannot be identified. |
| Form ID | meta_form_id | Text | Hold if the form cannot be identified. |
| Ad and ad set IDs | Source attribution fields | Text | Preserve as Unknown when the test origin cannot provide them. |
| Created time | source_created_at | Timestamp with timezone | Hold latency reporting until a valid timestamp exists. |
| Work email | email | Reject or route to review under the team's supplied rule. | |
| Company | company_name | Text | Request the missing value if the chosen route requires it. |
| Employee range | employee_range | Controlled text | Keep the supplied option exactly; do not translate it silently. |
| Planning stage | planning_stage | Controlled text | Route Unknown to the named owner. |
| Disclaimer response | Dedicated consent/disclaimer field | Text or Boolean per approved contract | Hold if the destination has no approved field. |
The destination must also create its own evidence: CRM record ID, received timestamp, assigned owner, deduplication result and any rejection reason. Those values do not come from a Meta settings screen.
Use a stable deduplication rule. For the fictional example, match on normalized work email within the same business unit. If an existing contact is found, update the source and campaign fields without creating a second contact, then record duplicate_matched and the existing CRM record ID. The exact rule is a business decision; the receipt should record what the team actually chose.
Retrieval and feedback are different jobs
Meta's short video presents four ways to connect lead management or feedback: a partner, Google Sheets, Leads Center in Meta Business Suite, or a direct/custom CRM integration. Watch from 0:45 for the route overview.
Watch “Boost lead quality with Meta Conversions API for CRM” from 0:45
The video helps choose an integration route. It does not show your field mapping, assignment, deduplication or destination receipt.
Meta's current Help documentation says the qualified-leads performance goal requires Conversions API for CRM for new campaigns from April 2026, with existing campaigns affected from August 2026. Recheck this in the account before launch. Do not select the goal because a document says the integration should exist. Select it only after the actual account and feedback path are verified.
Run two tests, not one
The Meta Lead Ads Testing Tool creates fake organic leads. Meta says they are not associated with an ad. That makes the tool suitable for a bounded transport test, but it cannot prove paid-ad source data.
Keep two separate rows in the delivery record:
- Transport test: Meta testing tool → webhook or connector → destination read-back.
- Source-origin test: controlled submission through the selected real source path → destination and owner read-back.
The source-origin test stays Hold until someone operates it through the real chosen route. Do not promote a transport test into source-origin proof.
Transport test: prove the path and received values
Use non-personal fixture values that are recognizable and safe in the destination.
- Open Meta's Lead Ads Testing Tool. Select the correct Page and Form before creating a lead.
- Preview the form when you need to inspect the current fields. Confirm the object before submitting.
- Choose custom test data and enter the fixture values. Keep the test marker in every field where the field type allows it.
- Create the test lead. Record the Meta lead ID and source created time. If the tool reports that a lead already exists for the selected form, delete the old test lead through the tool before creating the next bounded fixture.
- Open Track Status to inspect the realtime update sent to the configured endpoint.
- Record the endpoint response and payload. An HTTP 200 and success state show that the realtime update reached the configured endpoint.
- Open the actual CRM, sheet or lead-management destination. Find the record by the supplied test value or Meta lead ID.
- Compare every sent and received value. Record the CRM record ID, received timestamp, timezone, latency, owner and duplicate result.
- If the endpoint succeeds but the record is missing or wrong, mark delivery Failed and assign the failure owner. The green endpoint state cannot pass the destination check.
What Track Status proves
It proves that the realtime update reached the configured endpoint and shows the response Meta received.
It does not prove:
- that the source was a paid ad;
- that the CRM stored the expected values;
- that a destination record ID exists;
- that an owner was assigned;
- that deduplication worked;
- that follow-up happened;
- that the lead was qualified; or
- that the CRM sent later status back to Meta.
Source-origin test: keep the Hold honest
After the ad, form, privacy, mapping and transport test are accepted, submit a bounded fixture through the actual selected source path. Record the source URL or object, ad and form identifiers when available, exact fixture values, destination record, owner and timestamps.
If the controlled paid source path has not been operated, record:
Source-origin test: Hold. Required evidence: one controlled submission through the selected real source path, followed by destination value and owner read-back.
That sentence is better than a green checkbox based on the wrong test.
Use states that describe what happened
| State | Observable event |
|---|---|
| Submitted | The source accepted the form submission. |
| Delivered | The destination contains a record with the expected source ID or fixture values. |
| Accepted | The destination kept the record and no required-field, mapping or policy rule rejected it. |
| Contacted | The assigned owner or system recorded the first approved outreach attempt. |
| Qualified | The CRM condition chosen by the business was observed and recorded. |
| Booked | A meeting record exists and links to the lead or contact. |
| Duplicate | The supplied deduplication rule matched an existing record and recorded the result. |
| Failed | A named step returned an error, missing record or value mismatch. |
| Held | A required decision, approval, owner or test has not been supplied. |
| Unknown | The event has not been observed or the evidence is unavailable. |
Do not calculate a rate by treating Held or Unknown as zero. Report the known population and the unresolved records separately.
Facebook Lead Ads implementation kit
Start with the completed fictional handoff. Copy the blank handoff and receipt for your campaign, then run the build or review prompt in a fresh model conversation.
Copy the complete build prompt
The route rules, field requirements, test boundary and missing-input behavior travel with the prompt.
REFERENCE SUMMARY
Facebook Lead Ads can use an Instant Form, a website form or an account-dependent hybrid path. Select the route from the next action. Instant Forms fit short native requests. Website routes fit booking, quotes, fuller product context or sensitive information. Keep the ad, form introduction and ending on one promise. Preserve campaign, ad set, ad, Page and form identity plus the exact tracking specification in the standalone handoff. Every collected field must have a purpose, destination, type, owner and failure behavior. Missing privacy approval, CRM destination or record owner blocks launch.
Lead retrieval moves Meta submissions to the business. Conversions API for CRM sends later CRM status back to Meta. Treat those as separate directions. Meta's Lead Ads Testing Tool creates fake organic leads that are not associated with an ad. Track Status and an HTTP 200 prove endpoint transport only. Delivery requires destination read-back of exact values, destination record ID, timestamps, timezone, latency, owner and duplicate result. Paid or selected real-source proof requires a separate controlled submission through that source path.
Source boundaries: current mechanics were checked against Meta for Business Help and Meta for Developers on 27 September 2026. Recheck account controls before launch. Never invent current account availability, approval, live IDs, legal conclusions, delivery or performance.
EXECUTABLE CONTRACT
The exact contract below controls version, route type, state enums, nested requirements and canonical request IDs. Do not shorten the version, translate state values, wrap the canonical record under supplied_input/assessment, or rename a canonical request. A derived request for a repeated row must use `req-field-<source>-<missing-key>`, `req-question-<zero-based-index>-<missing-key>` or `req-map-<source>-<missing-key>`.
{
"contract_version": "1.0.0",
"routes": [
"INSTANT_FORM",
"WEBSITE",
"HYBRID"
],
"readiness": [
"DRAFT_READY",
"DRAFT_HOLD",
"FORM_READY",
"FORM_HOLD",
"CRM_READY",
"CRM_HOLD",
"TRANSPORT_VERIFIED",
"TRANSPORT_HOLD",
"SOURCE_ORIGIN_VERIFIED",
"SOURCE_ORIGIN_HOLD",
"LAUNCH_READY",
"LAUNCH_HOLD",
"TRANSPORT_FAILED",
"SOURCE_ORIGIN_FAILED"
],
"semantic_unresolved": [
"",
"unknown",
"pending",
"not supplied",
"not provided",
"not approved",
"not applicable",
"not_applicable",
"approval was not granted",
"permission was not granted",
"evidence was not supplied",
"tbd",
"placeholder",
"no controlled source submission",
"no real submission",
"no submission occurred",
"no submission was performed"
],
"normalized_record_schema": {
"top_level": [
"contract_version",
"fixture_id",
"fictional",
"route",
"route_reason",
"audience",
"offer",
"promised_next_action",
"campaign",
"ad",
"form",
"website",
"privacy",
"crm",
"transport",
"source_origin",
"states",
"requests"
],
"states": {
"draft": "DRAFT_READY|DRAFT_HOLD",
"form": "FORM_READY|FORM_HOLD",
"crm": "CRM_READY|CRM_HOLD",
"transport": "TRANSPORT_VERIFIED|TRANSPORT_HOLD|TRANSPORT_FAILED",
"source_origin": "SOURCE_ORIGIN_VERIFIED|SOURCE_ORIGIN_HOLD|SOURCE_ORIGIN_FAILED",
"launch": "LAUNCH_READY|LAUNCH_HOLD"
},
"request_keys": [
"request_id",
"field",
"owner",
"action"
],
"route_type": "string enum: INSTANT_FORM, WEBSITE or HYBRID",
"replay": "The normalized JSON is the complete canonical record at top level. It can be passed directly to the evaluator without unwrapping. states and requests are derived output fields.",
"hybrid_branches": {
"paths": [
"instant_form",
"website"
],
"transport_path": "transport.branches.<branch>",
"transport_required": [
"origin",
"test_kind",
"source_lead_id",
"sent_at",
"sent_values",
"received_values",
"destination_record_id",
"received_at",
"latency",
"assigned_owner",
"duplicate_result"
],
"source_origin_path": "source_origin.branches.<branch>",
"source_origin_required": [
"state",
"test_kind",
"selected_source_path",
"source_lead_id",
"submitted_at",
"sent_values",
"received_values",
"destination_record_id",
"received_at",
"latency",
"owner",
"assigned_owner",
"duplicate_result",
"evidence"
],
"map_rule": "Each branch independently supplies every applicable CRM map source and destination read-back, exact value and type, and expected-owner comparison. No branch inherits a sibling or root result.",
"state_rule": "Missing or semantically unresolved evidence holds that branch. A supplied value, type or owner mismatch fails that branch. Overall launch is ready only if both branches pass transport and selected real-source proof.",
"request_rule": "Use req-<branch-hyphenated>-transport-<suffix> and req-<branch-hyphenated>-source-origin-<suffix>, with dotted fields under the exact branch path.",
"source_origin_request_actions": {
"selected_source_path": "Record observed selected source path for this branch's selected real-source submission.",
"source_lead_id": "Record observed source lead id for this branch's selected real-source submission.",
"destination_record_id": "Record observed destination record id for this branch's selected real-source submission.",
"owner": "Record observed owner for this branch's selected real-source submission.",
"assigned_owner": "Record observed assigned owner for this branch's selected real-source submission.",
"submitted_at": "Record observed submitted at for this branch's selected real-source submission.",
"received_at": "Record observed received at for this branch's selected real-source submission.",
"latency": "Record observed latency for this branch's selected real-source submission.",
"duplicate_result": "Record observed duplicate result for this branch's selected real-source submission.",
"evidence": "Record observed evidence for this branch's selected real-source submission.",
"state": "Operate the selected real source path and record its verified state.",
"test_kind": "Identify the controlled selected real-source submission; a transport test cannot prove it.",
"test_kind_mismatch": "Operate the selected real-source path; a different test kind cannot prove this branch.",
"sent_values": "Record values sent through this selected real-source path.",
"received_values": "Read back values received from this selected real-source path.",
"mapped_sent": "Record the selected-source sent value for <source>.",
"mapped_sent_unresolved": "Supply an observed, resolved selected-source sent value for <source>.",
"mapped_readback": "Read back the selected-source mapped destination for <source>.",
"mapped_readback_unresolved": "Read back an observed, resolved selected-source destination value for <source>.",
"mapped_mismatch": "Repair the selected-source mapped value mismatch for <source>.",
"owner_mismatch": "Route the selected-source record to the expected CRM record owner, then read it back again."
},
"transport_request_actions": {
"test_kind": "Record the <branch-with-underscores-changed-to-spaces> branch transport test kind.",
"test_kind_mismatch": "Run and record the <branch-with-underscores-changed-to-spaces> branch's own <expected-kind> transport test.",
"mapped_sent": "Record the sent value for every applicable mapped source, including <source>.",
"mapped_sent_unresolved": "Supply an observed, resolved sent value for <source>; a placeholder does not prove delivery.",
"mapped_readback": "Read back the mapped destination value for <source>.",
"mapped_readback_unresolved": "Read back an observed, resolved destination value for <source>.",
"mapped_mismatch": "Repair and retest the <source> to <destination> value mismatch.",
"owner_mismatch": "Route the record to the expected CRM record owner, then read back the assigned owner again.",
"origin": "Name the test origin.",
"source_lead_id": "Record the source lead ID.",
"destination_record_id": "Record the destination record ID.",
"assigned_owner": "Read back the assigned owner.",
"duplicate_result": "Record the deduplication result.",
"sent_values": "Record every sent value.",
"received_values": "Read back every received value.",
"sent_at": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"received_at": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"latency": "Record timezone-aware sent and received timestamps plus latency in seconds."
},
"transport_expected_test_kinds": {
"instant_form": "META_TEST_TOOL_ORGANIC",
"website": "WEBSITE_CONTROLLED_TEST"
}
},
"human_readable_handoff": {
"required": true,
"states": "Copy the six canonical states exactly.",
"identity_and_tracking": [
"campaign_id",
"ad_set_id",
"ad_id",
"page_id",
"form_id",
"tracking_specification"
],
"sections": [
"route_and_offer",
"message_contract",
"selected_route_details",
"fields_and_qualification",
"privacy_and_consent",
"crm_handoff",
"test_states",
"open_requests"
],
"rule": "Include this object and a visible ## Human-readable implementation handoff section in every output, including Hold and Failed cases."
}
},
"source_evidence_denial_rule": "For source_origin.evidence or hybrid branch evidence, treat a grammatical negation of the controlled, real, live or selected-source operation as unresolved even when a state claims VERIFIED. Evaluate the negation, operation and source object independently of the sentence subject, voice, tense or intervening adverbs. For a standalone route, a VERIFIED claim additionally requires affirmative past-tense evidence that the controlled/real/live/selected operation completed and that the destination or CRM read-back/receipt completed. Planned, scheduled, future, awaiting or pending language stays unresolved; arbitrary supplied prose and a bare submission noun cannot prove operation. Hybrid branches also require structured selected-source IDs, values, owners and timestamps. Keep SOURCE_ORIGIN_HOLD and emit the exact proof/evidence request. A statement that no duplicate submission occurred is not a source-test denial and remains the explicit negative control.",
"nested_requirements": {
"form.fields": [
"source",
"purpose",
"destination",
"type",
"owner",
"failure_behavior"
],
"form.questions": [
"question",
"options",
"decision",
"source",
"destination",
"type",
"owner",
"failure_behavior"
],
"crm.field_map": [
"source",
"destination",
"type",
"transformation",
"failure_behavior"
]
},
"nested_request_actions": {
"form.fields": {
"source": "Name the source field.",
"purpose": "State why the field is collected.",
"destination": "Name the destination field.",
"type": "Name the field type.",
"owner": "Name the field owner.",
"failure_behavior": "Define missing or invalid value behavior."
},
"form.questions": {
"question": "Supply the qualification question.",
"options": "Supply its controlled options.",
"decision": "State the decision this answer supports.",
"source": "Bind the question to a source key.",
"destination": "Bind the answer to a destination field.",
"type": "Name the answer type.",
"owner": "Name the routing owner.",
"failure_behavior": "Define missing or invalid answer behavior."
},
"crm.field_map": {
"source": "Name the source value.",
"destination": "Name the destination field.",
"type": "Name the destination type.",
"transformation": "Define the transformation or explicitly state None.",
"failure_behavior": "Define missing or invalid value behavior."
}
},
"canonical_requests": {
"route": {
"request_id": "req-route",
"owner": "draft",
"action": "Choose INSTANT_FORM, WEBSITE or HYBRID."
},
"audience": {
"request_id": "req-audience",
"owner": "draft",
"action": "Name the exact audience."
},
"offer": {
"request_id": "req-offer",
"owner": "draft",
"action": "Name the exact resource or action."
},
"promised_next_action": {
"request_id": "req-next-action",
"owner": "draft",
"action": "State what happens immediately after submission."
},
"ad.opening": {
"request_id": "req-ad-opening",
"owner": "draft",
"action": "Supply the ad opening."
},
"ad.headline": {
"request_id": "req-ad-headline",
"owner": "draft",
"action": "Supply the ad headline."
},
"form.introduction": {
"request_id": "req-form-intro",
"owner": "draft",
"action": "Supply the form or page introduction."
},
"form.ending": {
"request_id": "req-ending",
"owner": "draft",
"action": "Supply the ending and destination action."
},
"campaign.campaign_id": {
"request_id": "req-campaign-id",
"owner": "draft",
"action": "Supply the campaign ID or keep launch on Hold until it exists."
},
"campaign.ad_set_id": {
"request_id": "req-ad-set-id",
"owner": "draft",
"action": "Supply the ad set ID or keep launch on Hold until it exists."
},
"campaign.ad_id": {
"request_id": "req-ad-id",
"owner": "draft",
"action": "Supply the ad ID or keep launch on Hold until it exists."
},
"campaign.tracking_specification": {
"request_id": "req-tracking-specification",
"owner": "draft",
"action": "Define the exact tracking parameters and object IDs to preserve."
},
"privacy.url": {
"request_id": "req-privacy-url",
"owner": "form",
"action": "Supply the final privacy-policy URL."
},
"privacy.approval_state": {
"request_id": "req-privacy-approval",
"owner": "form",
"action": "Provide an affirmative privacy approval state and ISO approval date."
},
"privacy.owner": {
"request_id": "req-privacy-owner",
"owner": "form",
"action": "Name the privacy and consent owner."
},
"form.fields": {
"request_id": "req-fields",
"owner": "form",
"action": "List every collected field and its purpose."
},
"form.questions": {
"request_id": "req-questions",
"owner": "form",
"action": "List each qualification question, options and decision."
},
"form.destination_url": {
"request_id": "req-destination-url",
"owner": "form",
"action": "Supply the final ending or website destination URL."
},
"privacy.notice": {
"request_id": "req-privacy-notice",
"owner": "form",
"action": "Supply the exact privacy notice or disclaimer text."
},
"privacy.approval_date": {
"request_id": "req-privacy-approval-date",
"owner": "form",
"action": "Supply the ISO approval date."
},
"privacy.disclaimer_destination": {
"request_id": "req-disclaimer-destination",
"owner": "form",
"action": "Map the disclaimer response separately from field_data."
},
"privacy.disclaimer_type": {
"request_id": "req-disclaimer-type",
"owner": "form",
"action": "Define the approved disclaimer response type."
},
"crm.destination": {
"request_id": "req-crm-destination",
"owner": "crm",
"action": "Name the CRM or lead-management destination."
},
"crm.record_owner": {
"request_id": "req-record-owner",
"owner": "crm",
"action": "Name the person or queue that owns a delivered record."
},
"crm.field_map": {
"request_id": "req-field-map",
"owner": "crm",
"action": "Map every source value to a destination field and type."
},
"crm.deduplication_rule": {
"request_id": "req-dedupe",
"owner": "crm",
"action": "Supply the deduplication rule."
},
"crm.response_target": {
"request_id": "req-response-target",
"owner": "crm",
"action": "Supply the response target, clock and timezone."
},
"crm.failure_owner": {
"request_id": "req-failure-owner",
"owner": "crm",
"action": "Name the owner of mapping or delivery failures."
},
"crm.escalation": {
"request_id": "req-escalation",
"owner": "crm",
"action": "Define the failure escalation path."
},
"transport.origin": {
"request_id": "req-test-origin",
"owner": "transport",
"action": "Name the test origin."
},
"transport.source_lead_id": {
"request_id": "req-source-id",
"owner": "transport",
"action": "Record the source lead ID."
},
"transport.sent_values": {
"request_id": "req-sent-values",
"owner": "transport",
"action": "Record every sent value."
},
"transport.received_values": {
"request_id": "req-received-values",
"owner": "transport",
"action": "Read back every received value."
},
"transport.destination_record_id": {
"request_id": "req-destination-id",
"owner": "transport",
"action": "Record the destination record ID."
},
"transport.received_at": {
"request_id": "req-received-at",
"owner": "transport",
"action": "Record the received timestamp and timezone."
},
"transport.latency": {
"request_id": "req-latency",
"owner": "transport",
"action": "Record the delivery latency with units."
},
"transport.assigned_owner": {
"request_id": "req-delivered-owner",
"owner": "transport",
"action": "Read back the assigned owner."
},
"transport.duplicate_result": {
"request_id": "req-duplicate-result",
"owner": "transport",
"action": "Record the deduplication result."
},
"transport.sent_at": {
"request_id": "req-sent-at",
"owner": "transport",
"action": "Record the sent timestamp and timezone."
},
"source_origin.evidence": {
"request_id": "req-source-origin-proof",
"owner": "source_origin",
"action": "Operate one controlled submission through the selected real source path and read it back in the destination."
},
"source_origin.owner": {
"request_id": "req-source-origin-owner",
"owner": "source_origin",
"action": "Name the person responsible for the controlled source-origin test."
}
},
"state_rules": {
"draft": "DRAFT_HOLD when any required draft path is unresolved; otherwise DRAFT_READY.",
"transport_missing_coverage": "TRANSPORT_HOLD unless transport is already failed.",
"transport_unresolved_mapped_value": "A semantically unresolved sent or read-back mapped value is TRANSPORT_HOLD even when both copies match. Boolean false and numeric zero are resolved and may be valid for their declared types.",
"transport_value_or_owner_mismatch": "TRANSPORT_FAILED.",
"hybrid": "Evaluate both transport and selected-source-origin branch objects independently. Ignore claimed verified labels when branch evidence is missing or contradictory. Failed beats Hold; Hold beats Verified.",
"receipt": "Render transport and source-origin state, failure and Hold rows from the evaluated result; show supplied result only as a clearly labeled claim.",
"launch": "LAUNCH_READY only when draft, form and CRM are ready and transport plus source origin are verified."
},
"transport_request_rules": {
"missing_timing": {
"request_ids": [
"req-transport-sent-at",
"req-transport-received-at",
"req-transport-latency"
],
"fields": [
"transport.sent_at",
"transport.received_at",
"transport.latency"
],
"owner": "transport",
"action_for_each": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"note": "These are separate derived requests. The canonical req-sent-at, req-received-at and req-latency use different actions and must also be present when their paths are unresolved."
},
"mapped_source_missing": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-sent",
"field": "transport.sent_values.<source>",
"owner": "transport",
"action": "Record the sent value for every applicable mapped source, including <source>."
},
"mapped_destination_missing": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-readback",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Read back the mapped destination value for <source>."
},
"mapped_source_unresolved": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-sent-unresolved",
"field": "transport.sent_values.<source>",
"owner": "transport",
"action": "Supply an observed, resolved sent value for <source>; a placeholder does not prove delivery."
},
"mapped_destination_unresolved": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-readback-unresolved",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Read back an observed, resolved destination value for <source>."
},
"mapped_value_or_type_mismatch": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-mismatch",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Repair and retest the <source> to <mapped-destination> value mismatch."
},
"assigned_owner_mismatch": {
"request_id": "req-transport-owner-mismatch",
"field": "transport.assigned_owner",
"owner": "transport",
"action": "Route the record to the expected CRM record owner, then read back the assigned owner again."
}
}
}
CANONICAL REQUEST CONSTRUCTION
Build `requests` from the supplied record with this exact algorithm. Do not add requests from your own judgment.
1. A value is unresolved only when it is null, an empty list/object, its normalized text starts with `unknown`, matches the executable contract's `semantic_unresolved` values, or contains one of: not supplied, not provided, not approved, was not granted, was not supplied, was not provided, evidence unavailable, permission denied, rejected, not yet approved, pending approval, approval denied, declined, no controlled real-source, no controlled real source, no controlled source-origin, no controlled source origin, no selected-source submission, no selected source submission, no controlled source submission, no real submission, no submission occurred, no submission was performed. A syntactically supplied URL, including a reserved example URL in a fictional fixture, counts as supplied. An elapsed-duration response target counts as supplied without a timezone; timezone validation applies to transport timestamps.
2. For every unresolved path in `canonical_requests`, copy its request_id, exact dotted field path, owner and action byte for byte. Never create a synonym request for a resolved path.
3. For each `form.fields` row at zero-based index i, set slug to its source lowercased with underscores changed to hyphens, or i when source is unresolved. For each unresolved nested key, emit `req-field-<slug>-<key-with-underscores-changed-to-hyphens>`, field `form.fields.<i>.<key>`, owner `form`, and the exact action in `nested_request_actions.form.fields`. If no `crm.field_map` row has the same source and destination, also emit `req-field-<slug>-mapping`, field `crm.field_map.<slug>`, owner `crm`, action `Map the field source to the same destination named in the form contract.`
4. For each `form.questions` row at zero-based index i, emit an exact nested request for every unresolved key using `req-question-<i>-<key-with-underscores-changed-to-hyphens>`, dotted field `form.questions.<i>.<key>`, owner `form`, and the exact action in `nested_request_actions.form.questions`. If source, destination and type do not bind exactly to one `form.fields` row, also emit `req-question-<i>-binding`, field `form.questions.<i>`, owner `form`, action `Bind the question source, destination and type to the matching collected field.`
5. For each `crm.field_map` row at zero-based index i, use the same source slug rule. For every unresolved nested key emit `req-map-<slug>-<key-with-underscores-changed-to-hyphens>`, field `crm.field_map.<i>.<key>`, owner `crm`, and the exact action in `nested_request_actions.crm.field_map`.
6. Apply the executable contract's route, privacy approval, complete-map transport comparison/timing, expected-owner comparison, string-ID, hybrid-branch and selected-real-source rules exactly. For each applicable `crm.field_map` row, use the exact templates in `transport_request_rules`: a missing source gets only the source-specific `-sent` request; a semantically unresolved present source gets `-sent-unresolved`; a missing mapped destination gets `-readback`; an unresolved present destination gets `-readback-unresolved`; a resolved value or type mismatch gets `-mismatch`. Equal Unknown, null or blank values remain Hold. Boolean false and permitted numeric zero remain resolved. Never substitute whole-object requests when only one member is missing or wrong. `transport.assigned_owner` must exactly match `crm.record_owner`; a supplied mismatch gets `req-transport-owner-mismatch` and makes transport TRANSPORT_FAILED. Preserve canonical IDs, fields, owners and actions byte for byte.
6a. For HYBRID, do not inherit evidence from root transport/source-origin or a sibling branch. For each of instant_form and website, require every transport field named in `normalized_record_schema.hybrid_branches.transport_required` under `transport.branches.<branch>`. Compare every declared map source and destination with exact value and type, the expected and observed owner, source/destination string IDs, timezone-aware timestamps and recomputed latency, and duplicate result. Require every field named in `source_origin_required` under `source_origin.branches.<branch>`, including selected path and evidence, and run the same map/owner/time checks on the selected real-source submission. A verified status label cannot override missing or contradictory evidence. Use branch-specific dotted fields and `req-<branch-hyphenated>-transport-...` or `req-<branch-hyphenated>-source-origin-...` IDs. Missing/unresolved branch evidence holds; mismatched values, types or owners fail; any branch Hold/Fail keeps overall LAUNCH_HOLD.
6b. Copy exact branch actions from `normalized_record_schema.hybrid_branches.source_origin_request_actions` and `transport_request_actions`. Do not invent a more helpful paraphrase. For a missing branch object, generate the full set of branch field requests plus applicable mapped source and destination requests; a single generic branch request is insufficient. For a present but wrong source `test_kind`, emit only the `-test-kind-mismatch` request for that field and mark SOURCE_ORIGIN_FAILED. For a missing test kind, emit the `-test-kind` request and hold.
For transport timing, every missing `sent_at`, `received_at` or `latency` request uses the single exact action `Record timezone-aware sent and received timestamps plus latency in seconds.` For a wrong transport test kind, the expected kind comes from `transport_expected_test_kinds`, never from the supplied wrong value: instant_form requires `META_TEST_TOOL_ORGANIC`; website requires `WEBSITE_CONTROLLED_TEST`.
6c. A missing whole transport value can have TWO distinct valid request IDs: the canonical whole-path request from `canonical_requests` and the transport-specific derived request. For example, `sent_values={}` emits both `req-sent-values` and `req-transport-sent-values`; `received_values={}` emits both `req-received-values` and `req-transport-received-values`. Do not suppress one because their fields match. This also applies to missing origin, IDs, owner, duplicate result and timestamps. Derived timing requests always use the SAME action from `transport_request_rules.missing_timing.action_for_each`; do not reuse the canonical timing request action. When `transport.duplicate_result` is missing, emit both `req-duplicate-result` and `req-transport-duplicate-result`; never collapse them because they share a field and action. De-duplicate only identical request IDs.
6d. Evaluate source evidence with `source_evidence_denial_rule`, even when `source_origin.state` claims VERIFIED. `No controlled source submission occurred.` and `No real submission was performed.` each require SOURCE_ORIGIN_HOLD and `req-source-origin-proof` for an Instant Form or website route. A hybrid branch uses its exact branch `-evidence` request. `No duplicate submission occurred` is not a denial of source operation.
7. De-duplicate by request_id with the last canonical object winning, then sort lexicographically by request_id.
Every canonical request object has exactly four keys in this order: `request_id`, `field`, `owner`, `action`. Never put reason, consequence, status, evidence or any other key inside `requests`. Put review consequences in a separate prose finding or separate `checks` entry. Use dotted paths, never bracket paths. Use hyphens in derived request IDs, never underscores.
STATE AND REQUEST PARITY EXAMPLES
- If any required draft path such as `campaign.campaign_id` is unresolved, states.draft is `DRAFT_HOLD` in JSON and prose. The presence of enough copy to draft cannot promote it.
- If `crm.field_map` maps `company_name` to `company_name` and only `transport.sent_values.company_name` is absent, emit exactly `{"request_id":"req-transport-company-name-sent","field":"transport.sent_values.company_name","owner":"transport","action":"Record the sent value for every applicable mapped source, including company_name."}`.
- If `custom_disclaimer_response` maps to `lead_form_notice_response` and only that destination read-back is absent, emit exactly `{"request_id":"req-transport-custom-disclaimer-response-readback","field":"transport.received_values.lead_form_notice_response","owner":"transport","action":"Read back the mapped destination value for custom_disclaimer_response."}`.
- If `transport.assigned_owner` is supplied but differs from `crm.record_owner`, emit exactly `{"request_id":"req-transport-owner-mismatch","field":"transport.assigned_owner","owner":"transport","action":"Route the record to the expected CRM record owner, then read back the assigned owner again."}`.
- If both sent and received `company_name` are `Unknown`, emit `req-transport-company-name-sent-unresolved` at `transport.sent_values.company_name` and keep TRANSPORT_HOLD; equality never verifies a placeholder.
- If the website branch of a HYBRID record has a wrong email, emit `req-website-transport-work-email-mismatch` at `transport.branches.website.received_values.email` and set TRANSPORT_FAILED even if the instant_form branch is complete.
- If a HYBRID website selected-source branch supplies `META_TEST_TOOL_ORGANIC` or any `test_kind` other than `SELECTED_SOURCE_ORIGIN`, emit exactly `req-website-source-origin-test-kind-mismatch`, set that branch and overall source origin to `SOURCE_ORIGIN_FAILED`, and keep launch on Hold. A testing-tool claim is a contradiction, not merely missing evidence.
- The wrong-kind request object is exactly `{"request_id":"req-website-source-origin-test-kind-mismatch","field":"source_origin.branches.website.test_kind","owner":"source_origin","action":"Operate the selected real-source path; a different test kind cannot prove this branch."}`. Do not replace it with `req-website-source-origin-test-kind` or a second evidence request when the evidence value itself is supplied.
- If `form.fields[0].source=work_email` has no matching CRM map row, emit exactly `{"request_id":"req-field-work-email-mapping","field":"crm.field_map.work-email","owner":"crm","action":"Map the field source to the same destination named in the form contract."}`. Only the request ID slug and its field-map slug use hyphens; the supplied source key stays `work_email` everywhere else.
- If all transport timestamps are unresolved, emit `req-sent-at`, `req-received-at` and `req-latency` with their distinct canonical actions AND `req-transport-sent-at`, `req-transport-received-at` and `req-transport-latency` with action exactly `Record timezone-aware sent and received timestamps plus latency in seconds.` for each derived request.
- A sentence such as “No controlled source submission occurred.” or “No real submission was performed.” is unresolved evidence even if a supplied state claims SOURCE_ORIGIN_VERIFIED. Emit the applicable source-origin proof request and keep launch on Hold.
- The top-level `requests` array is authoritative. Derive every prose open-request list from those exact objects. Never invent, omit, rename, paraphrase or normalize a canonical request ID, field, owner or action in a second pass.
TASK
Build a complete Facebook Lead Ads implementation handoff and delivery-test plan from the supplied inputs. Make a provisional draft when the message can be drafted safely. Keep launch on Hold when any launch fact is missing.
REQUIRED INPUTS
1. Audience, offer and promised next action.
2. Preferred route and reason, plus campaign, ad set, ad, Page and form IDs and the exact tracking specification. Unknown IDs keep launch on Hold.
3. Ad opening, headline and CTA.
4. Form or page introduction, fields, qualification questions, ending and destination.
5. Privacy-policy URL, notice or disclaimer text, approval state/date and owner.
6. CRM destination, complete field map, record owner, deduplication rule, response target and failure owner.
7. Transport fixture and any observed destination read-back.
8. Source-origin test owner and evidence, if operated.
MISSING-INPUT BEHAVIOR
- Do not invent a value, approval, ID, owner, URL, question option, CRM field, result or account control.
- Treat blank, Unknown, pending, not supplied, not approved, “permission was not granted” and similar natural negative statements as unresolved.
- Return one stable request per missing field using lowercase `req-` IDs derived from the field name.
- Missing privacy approval, CRM destination or record owner must produce `LAUNCH_HOLD`.
- A Meta testing-tool receipt may produce `TRANSPORT_VERIFIED` only after destination read-back. It must keep `SOURCE_ORIGIN_HOLD`.
- Off-route fields become `NOT_APPLICABLE`; they cannot hold the selected route. HYBRID requires both route branches.
OUTPUT CONTRACT
Return these sections in order:
1. Normalized JSON record that preserves all supplied machine IDs and values byte for byte. The JSON must be the complete canonical record at top level, use contract_version `1.0.0`, use a route enum string, include all six exact state values and include full request objects with exactly request_id, field, owner and action. It must replay directly through the evaluator without unwrapping or manual mapping. Construct this record first.
2. Route decision and reason derived from that same record.
3. Human-readable implementation handoff derived from that same record, with DRAFT, FORM, CRM, TRANSPORT, SOURCE_ORIGIN and LAUNCH states.
4. Open requests with request ID, field, owner, reason, exact action and clearing evidence. Consequences and clearing evidence belong here, never inside the canonical request objects.
Use the exact lowercase state keys `draft`, `form`, `crm`, `transport`, `source_origin`, `launch` and exact state vocabulary: DRAFT_READY/DRAFT_HOLD, FORM_READY/FORM_HOLD, CRM_READY/CRM_HOLD, TRANSPORT_VERIFIED/TRANSPORT_HOLD/TRANSPORT_FAILED, SOURCE_ORIGIN_VERIFIED/SOURCE_ORIGIN_HOLD/SOURCE_ORIGIN_FAILED and LAUNCH_READY/LAUNCH_HOLD. Separate drafting from launch. Preserve every supplied fact exactly unless you identify a contradiction and request a decision.
The human-readable handoff is mandatory in every response, including Hold and Failed cases, and must repeat the supplied campaign ID, ad set ID, ad ID, Page ID, form ID and tracking specification verbatim. Also place it in the top-level JSON field `human_readable_handoff` so it cannot be dropped during structured output. That object must contain `states` copied byte for byte from the canonical `states` object, an `identity_and_tracking` object containing campaign_id, ad_set_id, ad_id, page_id, form_id and tracking_specification copied verbatim, and eight named sections from `normalized_record_schema.human_readable_handoff.sections`. Print a visible `## Human-readable implementation handoff` section after the JSON with the same six states and identity/tracking values. Never compute a second state set.
When draft essentials are missing, return DRAFT_HOLD and a provisional Hold handoff with Unknown for missing values. Still include every canonical request, including both whole-path and derived transport requests. Ask no more than five focused questions in the prose. Never fabricate a completed handoff.
FINAL SELF-CHECK BEFORE RETURNING
- Compare the complete `requests` array against every unresolved canonical path, every nested row, every route field, every transport field and every hybrid branch. Do not stop after the first five requests.
- Copy exact request actions from the embedded contract. Preserve distinct IDs even when two requests have the same field.
- Sort the final `requests` array by the complete `request_id` string in ascending bytewise lexicographic order immediately before serializing JSON. Check every adjacent pair. For example, `req-failure-owner` comes before every `req-field-...` ID because `a` comes before `i`; never group requests by topic or owner.
- A supplied wrong hybrid source-test kind is FAILED with `-test-kind-mismatch`; an absent kind is HOLD with `-test-kind`.
- Print the visible human handoff and its JSON object in all cases. If either is missing, the response is incomplete.
SUPPLIED INPUTS
Paste the complete business and campaign inputs here.
Copy the complete review prompt
Paste the full handoff and receipt below this prompt. It keeps privacy, destination and source-origin gaps on Hold.
REFERENCE SUMMARY
Facebook Lead Ads can use an Instant Form, a website form or an account-dependent hybrid path. Select the route from the next action. Instant Forms fit short native requests. Website routes fit booking, quotes, fuller product context or sensitive information. Keep the ad, form introduction and ending on one promise. Preserve campaign, ad set, ad, Page and form identity plus the exact tracking specification in the standalone handoff. Every collected field must have a purpose, destination, type, owner and failure behavior. Missing privacy approval, CRM destination or record owner blocks launch.
Lead retrieval moves Meta submissions to the business. Conversions API for CRM sends later CRM status back to Meta. Treat those as separate directions. Meta's Lead Ads Testing Tool creates fake organic leads that are not associated with an ad. Track Status and an HTTP 200 prove endpoint transport only. Delivery requires destination read-back of exact values, destination record ID, timestamps, timezone, latency, owner and duplicate result. Paid or selected real-source proof requires a separate controlled submission through that source path.
Source boundaries: current mechanics were checked against Meta for Business Help and Meta for Developers on 27 September 2026. Recheck account controls before launch. Never invent current account availability, approval, live IDs, legal conclusions, delivery or performance.
EXECUTABLE CONTRACT
The exact contract below controls version, route type, state enums, nested requirements and canonical request IDs. Do not shorten the version, translate state values, wrap the canonical record under supplied_input/assessment, or rename a canonical request. A derived request for a repeated row must use `req-field-<source>-<missing-key>`, `req-question-<zero-based-index>-<missing-key>` or `req-map-<source>-<missing-key>`.
{
"contract_version": "1.0.0",
"routes": [
"INSTANT_FORM",
"WEBSITE",
"HYBRID"
],
"readiness": [
"DRAFT_READY",
"DRAFT_HOLD",
"FORM_READY",
"FORM_HOLD",
"CRM_READY",
"CRM_HOLD",
"TRANSPORT_VERIFIED",
"TRANSPORT_HOLD",
"SOURCE_ORIGIN_VERIFIED",
"SOURCE_ORIGIN_HOLD",
"LAUNCH_READY",
"LAUNCH_HOLD",
"TRANSPORT_FAILED",
"SOURCE_ORIGIN_FAILED"
],
"semantic_unresolved": [
"",
"unknown",
"pending",
"not supplied",
"not provided",
"not approved",
"not applicable",
"not_applicable",
"approval was not granted",
"permission was not granted",
"evidence was not supplied",
"tbd",
"placeholder",
"no controlled source submission",
"no real submission",
"no submission occurred",
"no submission was performed"
],
"normalized_record_schema": {
"top_level": [
"contract_version",
"fixture_id",
"fictional",
"route",
"route_reason",
"audience",
"offer",
"promised_next_action",
"campaign",
"ad",
"form",
"website",
"privacy",
"crm",
"transport",
"source_origin",
"states",
"requests"
],
"states": {
"draft": "DRAFT_READY|DRAFT_HOLD",
"form": "FORM_READY|FORM_HOLD",
"crm": "CRM_READY|CRM_HOLD",
"transport": "TRANSPORT_VERIFIED|TRANSPORT_HOLD|TRANSPORT_FAILED",
"source_origin": "SOURCE_ORIGIN_VERIFIED|SOURCE_ORIGIN_HOLD|SOURCE_ORIGIN_FAILED",
"launch": "LAUNCH_READY|LAUNCH_HOLD"
},
"request_keys": [
"request_id",
"field",
"owner",
"action"
],
"route_type": "string enum: INSTANT_FORM, WEBSITE or HYBRID",
"replay": "The normalized JSON is the complete canonical record at top level. It can be passed directly to the evaluator without unwrapping. states and requests are derived output fields.",
"hybrid_branches": {
"paths": [
"instant_form",
"website"
],
"transport_path": "transport.branches.<branch>",
"transport_required": [
"origin",
"test_kind",
"source_lead_id",
"sent_at",
"sent_values",
"received_values",
"destination_record_id",
"received_at",
"latency",
"assigned_owner",
"duplicate_result"
],
"source_origin_path": "source_origin.branches.<branch>",
"source_origin_required": [
"state",
"test_kind",
"selected_source_path",
"source_lead_id",
"submitted_at",
"sent_values",
"received_values",
"destination_record_id",
"received_at",
"latency",
"owner",
"assigned_owner",
"duplicate_result",
"evidence"
],
"map_rule": "Each branch independently supplies every applicable CRM map source and destination read-back, exact value and type, and expected-owner comparison. No branch inherits a sibling or root result.",
"state_rule": "Missing or semantically unresolved evidence holds that branch. A supplied value, type or owner mismatch fails that branch. Overall launch is ready only if both branches pass transport and selected real-source proof.",
"request_rule": "Use req-<branch-hyphenated>-transport-<suffix> and req-<branch-hyphenated>-source-origin-<suffix>, with dotted fields under the exact branch path.",
"source_origin_request_actions": {
"selected_source_path": "Record observed selected source path for this branch's selected real-source submission.",
"source_lead_id": "Record observed source lead id for this branch's selected real-source submission.",
"destination_record_id": "Record observed destination record id for this branch's selected real-source submission.",
"owner": "Record observed owner for this branch's selected real-source submission.",
"assigned_owner": "Record observed assigned owner for this branch's selected real-source submission.",
"submitted_at": "Record observed submitted at for this branch's selected real-source submission.",
"received_at": "Record observed received at for this branch's selected real-source submission.",
"latency": "Record observed latency for this branch's selected real-source submission.",
"duplicate_result": "Record observed duplicate result for this branch's selected real-source submission.",
"evidence": "Record observed evidence for this branch's selected real-source submission.",
"state": "Operate the selected real source path and record its verified state.",
"test_kind": "Identify the controlled selected real-source submission; a transport test cannot prove it.",
"test_kind_mismatch": "Operate the selected real-source path; a different test kind cannot prove this branch.",
"sent_values": "Record values sent through this selected real-source path.",
"received_values": "Read back values received from this selected real-source path.",
"mapped_sent": "Record the selected-source sent value for <source>.",
"mapped_sent_unresolved": "Supply an observed, resolved selected-source sent value for <source>.",
"mapped_readback": "Read back the selected-source mapped destination for <source>.",
"mapped_readback_unresolved": "Read back an observed, resolved selected-source destination value for <source>.",
"mapped_mismatch": "Repair the selected-source mapped value mismatch for <source>.",
"owner_mismatch": "Route the selected-source record to the expected CRM record owner, then read it back again."
},
"transport_request_actions": {
"test_kind": "Record the <branch-with-underscores-changed-to-spaces> branch transport test kind.",
"test_kind_mismatch": "Run and record the <branch-with-underscores-changed-to-spaces> branch's own <expected-kind> transport test.",
"mapped_sent": "Record the sent value for every applicable mapped source, including <source>.",
"mapped_sent_unresolved": "Supply an observed, resolved sent value for <source>; a placeholder does not prove delivery.",
"mapped_readback": "Read back the mapped destination value for <source>.",
"mapped_readback_unresolved": "Read back an observed, resolved destination value for <source>.",
"mapped_mismatch": "Repair and retest the <source> to <destination> value mismatch.",
"owner_mismatch": "Route the record to the expected CRM record owner, then read back the assigned owner again.",
"origin": "Name the test origin.",
"source_lead_id": "Record the source lead ID.",
"destination_record_id": "Record the destination record ID.",
"assigned_owner": "Read back the assigned owner.",
"duplicate_result": "Record the deduplication result.",
"sent_values": "Record every sent value.",
"received_values": "Read back every received value.",
"sent_at": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"received_at": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"latency": "Record timezone-aware sent and received timestamps plus latency in seconds."
},
"transport_expected_test_kinds": {
"instant_form": "META_TEST_TOOL_ORGANIC",
"website": "WEBSITE_CONTROLLED_TEST"
}
},
"human_readable_handoff": {
"required": true,
"states": "Copy the six canonical states exactly.",
"identity_and_tracking": [
"campaign_id",
"ad_set_id",
"ad_id",
"page_id",
"form_id",
"tracking_specification"
],
"sections": [
"route_and_offer",
"message_contract",
"selected_route_details",
"fields_and_qualification",
"privacy_and_consent",
"crm_handoff",
"test_states",
"open_requests"
],
"rule": "Include this object and a visible ## Human-readable implementation handoff section in every output, including Hold and Failed cases."
}
},
"source_evidence_denial_rule": "For source_origin.evidence or hybrid branch evidence, treat a grammatical negation of the controlled, real, live or selected-source operation as unresolved even when a state claims VERIFIED. Evaluate the negation, operation and source object independently of the sentence subject, voice, tense or intervening adverbs. For a standalone route, a VERIFIED claim additionally requires affirmative past-tense evidence that the controlled/real/live/selected operation completed and that the destination or CRM read-back/receipt completed. Planned, scheduled, future, awaiting or pending language stays unresolved; arbitrary supplied prose and a bare submission noun cannot prove operation. Hybrid branches also require structured selected-source IDs, values, owners and timestamps. Keep SOURCE_ORIGIN_HOLD and emit the exact proof/evidence request. A statement that no duplicate submission occurred is not a source-test denial and remains the explicit negative control.",
"nested_requirements": {
"form.fields": [
"source",
"purpose",
"destination",
"type",
"owner",
"failure_behavior"
],
"form.questions": [
"question",
"options",
"decision",
"source",
"destination",
"type",
"owner",
"failure_behavior"
],
"crm.field_map": [
"source",
"destination",
"type",
"transformation",
"failure_behavior"
]
},
"nested_request_actions": {
"form.fields": {
"source": "Name the source field.",
"purpose": "State why the field is collected.",
"destination": "Name the destination field.",
"type": "Name the field type.",
"owner": "Name the field owner.",
"failure_behavior": "Define missing or invalid value behavior."
},
"form.questions": {
"question": "Supply the qualification question.",
"options": "Supply its controlled options.",
"decision": "State the decision this answer supports.",
"source": "Bind the question to a source key.",
"destination": "Bind the answer to a destination field.",
"type": "Name the answer type.",
"owner": "Name the routing owner.",
"failure_behavior": "Define missing or invalid answer behavior."
},
"crm.field_map": {
"source": "Name the source value.",
"destination": "Name the destination field.",
"type": "Name the destination type.",
"transformation": "Define the transformation or explicitly state None.",
"failure_behavior": "Define missing or invalid value behavior."
}
},
"canonical_requests": {
"route": {
"request_id": "req-route",
"owner": "draft",
"action": "Choose INSTANT_FORM, WEBSITE or HYBRID."
},
"audience": {
"request_id": "req-audience",
"owner": "draft",
"action": "Name the exact audience."
},
"offer": {
"request_id": "req-offer",
"owner": "draft",
"action": "Name the exact resource or action."
},
"promised_next_action": {
"request_id": "req-next-action",
"owner": "draft",
"action": "State what happens immediately after submission."
},
"ad.opening": {
"request_id": "req-ad-opening",
"owner": "draft",
"action": "Supply the ad opening."
},
"ad.headline": {
"request_id": "req-ad-headline",
"owner": "draft",
"action": "Supply the ad headline."
},
"form.introduction": {
"request_id": "req-form-intro",
"owner": "draft",
"action": "Supply the form or page introduction."
},
"form.ending": {
"request_id": "req-ending",
"owner": "draft",
"action": "Supply the ending and destination action."
},
"campaign.campaign_id": {
"request_id": "req-campaign-id",
"owner": "draft",
"action": "Supply the campaign ID or keep launch on Hold until it exists."
},
"campaign.ad_set_id": {
"request_id": "req-ad-set-id",
"owner": "draft",
"action": "Supply the ad set ID or keep launch on Hold until it exists."
},
"campaign.ad_id": {
"request_id": "req-ad-id",
"owner": "draft",
"action": "Supply the ad ID or keep launch on Hold until it exists."
},
"campaign.tracking_specification": {
"request_id": "req-tracking-specification",
"owner": "draft",
"action": "Define the exact tracking parameters and object IDs to preserve."
},
"privacy.url": {
"request_id": "req-privacy-url",
"owner": "form",
"action": "Supply the final privacy-policy URL."
},
"privacy.approval_state": {
"request_id": "req-privacy-approval",
"owner": "form",
"action": "Provide an affirmative privacy approval state and ISO approval date."
},
"privacy.owner": {
"request_id": "req-privacy-owner",
"owner": "form",
"action": "Name the privacy and consent owner."
},
"form.fields": {
"request_id": "req-fields",
"owner": "form",
"action": "List every collected field and its purpose."
},
"form.questions": {
"request_id": "req-questions",
"owner": "form",
"action": "List each qualification question, options and decision."
},
"form.destination_url": {
"request_id": "req-destination-url",
"owner": "form",
"action": "Supply the final ending or website destination URL."
},
"privacy.notice": {
"request_id": "req-privacy-notice",
"owner": "form",
"action": "Supply the exact privacy notice or disclaimer text."
},
"privacy.approval_date": {
"request_id": "req-privacy-approval-date",
"owner": "form",
"action": "Supply the ISO approval date."
},
"privacy.disclaimer_destination": {
"request_id": "req-disclaimer-destination",
"owner": "form",
"action": "Map the disclaimer response separately from field_data."
},
"privacy.disclaimer_type": {
"request_id": "req-disclaimer-type",
"owner": "form",
"action": "Define the approved disclaimer response type."
},
"crm.destination": {
"request_id": "req-crm-destination",
"owner": "crm",
"action": "Name the CRM or lead-management destination."
},
"crm.record_owner": {
"request_id": "req-record-owner",
"owner": "crm",
"action": "Name the person or queue that owns a delivered record."
},
"crm.field_map": {
"request_id": "req-field-map",
"owner": "crm",
"action": "Map every source value to a destination field and type."
},
"crm.deduplication_rule": {
"request_id": "req-dedupe",
"owner": "crm",
"action": "Supply the deduplication rule."
},
"crm.response_target": {
"request_id": "req-response-target",
"owner": "crm",
"action": "Supply the response target, clock and timezone."
},
"crm.failure_owner": {
"request_id": "req-failure-owner",
"owner": "crm",
"action": "Name the owner of mapping or delivery failures."
},
"crm.escalation": {
"request_id": "req-escalation",
"owner": "crm",
"action": "Define the failure escalation path."
},
"transport.origin": {
"request_id": "req-test-origin",
"owner": "transport",
"action": "Name the test origin."
},
"transport.source_lead_id": {
"request_id": "req-source-id",
"owner": "transport",
"action": "Record the source lead ID."
},
"transport.sent_values": {
"request_id": "req-sent-values",
"owner": "transport",
"action": "Record every sent value."
},
"transport.received_values": {
"request_id": "req-received-values",
"owner": "transport",
"action": "Read back every received value."
},
"transport.destination_record_id": {
"request_id": "req-destination-id",
"owner": "transport",
"action": "Record the destination record ID."
},
"transport.received_at": {
"request_id": "req-received-at",
"owner": "transport",
"action": "Record the received timestamp and timezone."
},
"transport.latency": {
"request_id": "req-latency",
"owner": "transport",
"action": "Record the delivery latency with units."
},
"transport.assigned_owner": {
"request_id": "req-delivered-owner",
"owner": "transport",
"action": "Read back the assigned owner."
},
"transport.duplicate_result": {
"request_id": "req-duplicate-result",
"owner": "transport",
"action": "Record the deduplication result."
},
"transport.sent_at": {
"request_id": "req-sent-at",
"owner": "transport",
"action": "Record the sent timestamp and timezone."
},
"source_origin.evidence": {
"request_id": "req-source-origin-proof",
"owner": "source_origin",
"action": "Operate one controlled submission through the selected real source path and read it back in the destination."
},
"source_origin.owner": {
"request_id": "req-source-origin-owner",
"owner": "source_origin",
"action": "Name the person responsible for the controlled source-origin test."
}
},
"state_rules": {
"draft": "DRAFT_HOLD when any required draft path is unresolved; otherwise DRAFT_READY.",
"transport_missing_coverage": "TRANSPORT_HOLD unless transport is already failed.",
"transport_unresolved_mapped_value": "A semantically unresolved sent or read-back mapped value is TRANSPORT_HOLD even when both copies match. Boolean false and numeric zero are resolved and may be valid for their declared types.",
"transport_value_or_owner_mismatch": "TRANSPORT_FAILED.",
"hybrid": "Evaluate both transport and selected-source-origin branch objects independently. Ignore claimed verified labels when branch evidence is missing or contradictory. Failed beats Hold; Hold beats Verified.",
"receipt": "Render transport and source-origin state, failure and Hold rows from the evaluated result; show supplied result only as a clearly labeled claim.",
"launch": "LAUNCH_READY only when draft, form and CRM are ready and transport plus source origin are verified."
},
"transport_request_rules": {
"missing_timing": {
"request_ids": [
"req-transport-sent-at",
"req-transport-received-at",
"req-transport-latency"
],
"fields": [
"transport.sent_at",
"transport.received_at",
"transport.latency"
],
"owner": "transport",
"action_for_each": "Record timezone-aware sent and received timestamps plus latency in seconds.",
"note": "These are separate derived requests. The canonical req-sent-at, req-received-at and req-latency use different actions and must also be present when their paths are unresolved."
},
"mapped_source_missing": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-sent",
"field": "transport.sent_values.<source>",
"owner": "transport",
"action": "Record the sent value for every applicable mapped source, including <source>."
},
"mapped_destination_missing": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-readback",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Read back the mapped destination value for <source>."
},
"mapped_source_unresolved": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-sent-unresolved",
"field": "transport.sent_values.<source>",
"owner": "transport",
"action": "Supply an observed, resolved sent value for <source>; a placeholder does not prove delivery."
},
"mapped_destination_unresolved": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-readback-unresolved",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Read back an observed, resolved destination value for <source>."
},
"mapped_value_or_type_mismatch": {
"request_id": "req-transport-<source-with-underscores-changed-to-hyphens>-mismatch",
"field": "transport.received_values.<mapped-destination>",
"owner": "transport",
"action": "Repair and retest the <source> to <mapped-destination> value mismatch."
},
"assigned_owner_mismatch": {
"request_id": "req-transport-owner-mismatch",
"field": "transport.assigned_owner",
"owner": "transport",
"action": "Route the record to the expected CRM record owner, then read back the assigned owner again."
}
}
}
CANONICAL REQUEST CONSTRUCTION
Build `requests` from the supplied record with this exact algorithm. Do not add requests from your own judgment.
1. A value is unresolved only when it is null, an empty list/object, its normalized text starts with `unknown`, matches the executable contract's `semantic_unresolved` values, or contains one of: not supplied, not provided, not approved, was not granted, was not supplied, was not provided, evidence unavailable, permission denied, rejected, not yet approved, pending approval, approval denied, declined, no controlled real-source, no controlled real source, no controlled source-origin, no controlled source origin, no selected-source submission, no selected source submission, no controlled source submission, no real submission, no submission occurred, no submission was performed. A syntactically supplied URL, including a reserved example URL in a fictional fixture, counts as supplied. An elapsed-duration response target counts as supplied without a timezone; timezone validation applies to transport timestamps.
2. For every unresolved path in `canonical_requests`, copy its request_id, exact dotted field path, owner and action byte for byte. Never create a synonym request for a resolved path.
3. For each `form.fields` row at zero-based index i, set slug to its source lowercased with underscores changed to hyphens, or i when source is unresolved. For each unresolved nested key, emit `req-field-<slug>-<key-with-underscores-changed-to-hyphens>`, field `form.fields.<i>.<key>`, owner `form`, and the exact action in `nested_request_actions.form.fields`. If no `crm.field_map` row has the same source and destination, also emit `req-field-<slug>-mapping`, field `crm.field_map.<slug>`, owner `crm`, action `Map the field source to the same destination named in the form contract.`
4. For each `form.questions` row at zero-based index i, emit an exact nested request for every unresolved key using `req-question-<i>-<key-with-underscores-changed-to-hyphens>`, dotted field `form.questions.<i>.<key>`, owner `form`, and the exact action in `nested_request_actions.form.questions`. If source, destination and type do not bind exactly to one `form.fields` row, also emit `req-question-<i>-binding`, field `form.questions.<i>`, owner `form`, action `Bind the question source, destination and type to the matching collected field.`
5. For each `crm.field_map` row at zero-based index i, use the same source slug rule. For every unresolved nested key emit `req-map-<slug>-<key-with-underscores-changed-to-hyphens>`, field `crm.field_map.<i>.<key>`, owner `crm`, and the exact action in `nested_request_actions.crm.field_map`.
6. Apply the executable contract's route, privacy approval, complete-map transport comparison/timing, expected-owner comparison, string-ID, hybrid-branch and selected-real-source rules exactly. For each applicable `crm.field_map` row, use the exact templates in `transport_request_rules`: a missing source gets only the source-specific `-sent` request; a semantically unresolved present source gets `-sent-unresolved`; a missing mapped destination gets `-readback`; an unresolved present destination gets `-readback-unresolved`; a resolved value or type mismatch gets `-mismatch`. Equal Unknown, null or blank values remain Hold. Boolean false and permitted numeric zero remain resolved. Never substitute whole-object requests when only one member is missing or wrong. `transport.assigned_owner` must exactly match `crm.record_owner`; a supplied mismatch gets `req-transport-owner-mismatch` and makes transport TRANSPORT_FAILED. Preserve canonical IDs, fields, owners and actions byte for byte.
6a. For HYBRID, do not inherit evidence from root transport/source-origin or a sibling branch. For each of instant_form and website, require every transport field named in `normalized_record_schema.hybrid_branches.transport_required` under `transport.branches.<branch>`. Compare every declared map source and destination with exact value and type, the expected and observed owner, source/destination string IDs, timezone-aware timestamps and recomputed latency, and duplicate result. Require every field named in `source_origin_required` under `source_origin.branches.<branch>`, including selected path and evidence, and run the same map/owner/time checks on the selected real-source submission. A verified status label cannot override missing or contradictory evidence. Use branch-specific dotted fields and `req-<branch-hyphenated>-transport-...` or `req-<branch-hyphenated>-source-origin-...` IDs. Missing/unresolved branch evidence holds; mismatched values, types or owners fail; any branch Hold/Fail keeps overall LAUNCH_HOLD.
6b. Copy exact branch actions from `normalized_record_schema.hybrid_branches.source_origin_request_actions` and `transport_request_actions`. Do not invent a more helpful paraphrase. For a missing branch object, generate the full set of branch field requests plus applicable mapped source and destination requests; a single generic branch request is insufficient. For a present but wrong source `test_kind`, emit only the `-test-kind-mismatch` request for that field and mark SOURCE_ORIGIN_FAILED. For a missing test kind, emit the `-test-kind` request and hold.
For transport timing, every missing `sent_at`, `received_at` or `latency` request uses the single exact action `Record timezone-aware sent and received timestamps plus latency in seconds.` For a wrong transport test kind, the expected kind comes from `transport_expected_test_kinds`, never from the supplied wrong value: instant_form requires `META_TEST_TOOL_ORGANIC`; website requires `WEBSITE_CONTROLLED_TEST`.
6c. A missing whole transport value can have TWO distinct valid request IDs: the canonical whole-path request from `canonical_requests` and the transport-specific derived request. For example, `sent_values={}` emits both `req-sent-values` and `req-transport-sent-values`; `received_values={}` emits both `req-received-values` and `req-transport-received-values`. Do not suppress one because their fields match. This also applies to missing origin, IDs, owner, duplicate result and timestamps. Derived timing requests always use the SAME action from `transport_request_rules.missing_timing.action_for_each`; do not reuse the canonical timing request action. When `transport.duplicate_result` is missing, emit both `req-duplicate-result` and `req-transport-duplicate-result`; never collapse them because they share a field and action. De-duplicate only identical request IDs.
6d. Evaluate source evidence with `source_evidence_denial_rule`, even when `source_origin.state` claims VERIFIED. `No controlled source submission occurred.` and `No real submission was performed.` each require SOURCE_ORIGIN_HOLD and `req-source-origin-proof` for an Instant Form or website route. A hybrid branch uses its exact branch `-evidence` request. `No duplicate submission occurred` is not a denial of source operation.
7. De-duplicate by request_id with the last canonical object winning, then sort lexicographically by request_id.
Every canonical request object has exactly four keys in this order: `request_id`, `field`, `owner`, `action`. Never put reason, consequence, status, evidence or any other key inside `requests`. Put review consequences in a separate prose finding or separate `checks` entry. Use dotted paths, never bracket paths. Use hyphens in derived request IDs, never underscores.
STATE AND REQUEST PARITY EXAMPLES
- If any required draft path such as `campaign.campaign_id` is unresolved, states.draft is `DRAFT_HOLD` in JSON and prose. The presence of enough copy to draft cannot promote it.
- If `crm.field_map` maps `company_name` to `company_name` and only `transport.sent_values.company_name` is absent, emit exactly `{"request_id":"req-transport-company-name-sent","field":"transport.sent_values.company_name","owner":"transport","action":"Record the sent value for every applicable mapped source, including company_name."}`.
- If `custom_disclaimer_response` maps to `lead_form_notice_response` and only that destination read-back is absent, emit exactly `{"request_id":"req-transport-custom-disclaimer-response-readback","field":"transport.received_values.lead_form_notice_response","owner":"transport","action":"Read back the mapped destination value for custom_disclaimer_response."}`.
- If `transport.assigned_owner` is supplied but differs from `crm.record_owner`, emit exactly `{"request_id":"req-transport-owner-mismatch","field":"transport.assigned_owner","owner":"transport","action":"Route the record to the expected CRM record owner, then read back the assigned owner again."}`.
- If both sent and received `company_name` are `Unknown`, emit `req-transport-company-name-sent-unresolved` at `transport.sent_values.company_name` and keep TRANSPORT_HOLD; equality never verifies a placeholder.
- If the website branch of a HYBRID record has a wrong email, emit `req-website-transport-work-email-mismatch` at `transport.branches.website.received_values.email` and set TRANSPORT_FAILED even if the instant_form branch is complete.
- If a HYBRID website selected-source branch supplies `META_TEST_TOOL_ORGANIC` or any `test_kind` other than `SELECTED_SOURCE_ORIGIN`, emit exactly `req-website-source-origin-test-kind-mismatch`, set that branch and overall source origin to `SOURCE_ORIGIN_FAILED`, and keep launch on Hold. A testing-tool claim is a contradiction, not merely missing evidence.
- The wrong-kind request object is exactly `{"request_id":"req-website-source-origin-test-kind-mismatch","field":"source_origin.branches.website.test_kind","owner":"source_origin","action":"Operate the selected real-source path; a different test kind cannot prove this branch."}`. Do not replace it with `req-website-source-origin-test-kind` or a second evidence request when the evidence value itself is supplied.
- If `form.fields[0].source=work_email` has no matching CRM map row, emit exactly `{"request_id":"req-field-work-email-mapping","field":"crm.field_map.work-email","owner":"crm","action":"Map the field source to the same destination named in the form contract."}`. Only the request ID slug and its field-map slug use hyphens; the supplied source key stays `work_email` everywhere else.
- If all transport timestamps are unresolved, emit `req-sent-at`, `req-received-at` and `req-latency` with their distinct canonical actions AND `req-transport-sent-at`, `req-transport-received-at` and `req-transport-latency` with action exactly `Record timezone-aware sent and received timestamps plus latency in seconds.` for each derived request.
- A sentence such as “No controlled source submission occurred.” or “No real submission was performed.” is unresolved evidence even if a supplied state claims SOURCE_ORIGIN_VERIFIED. Emit the applicable source-origin proof request and keep launch on Hold.
- The top-level `requests` array is authoritative. Derive every prose open-request list from those exact objects. Never invent, omit, rename, paraphrase or normalize a canonical request ID, field, owner or action in a second pass.
TASK
Review a Facebook Lead Ads implementation handoff and delivery receipt. Decide whether another teammate can implement the route and whether the available evidence supports launch.
REVIEW RULES
1. Recompute the route from the required next action. Check INSTANT_FORM, WEBSITE and HYBRID independently.
2. Check that audience, offer, ad opening, headline, form introduction, questions, ending and destination keep one promise.
3. Check each field for purpose, destination, type and missing-value behavior.
4. Require the privacy URL, exact notice or disclaimer, approval state/date and owner. Do not give legal advice or convert a placeholder into approval.
5. Require CRM destination, exact field map, record owner, deduplication, response target, failure owner and escalation.
6. For transport, require every applicable CRM map source in sent values, compare each mapped destination value and type, and compare assigned owner with the expected CRM record owner. Require source ID, destination record ID, timestamps, timezone, latency and duplicate result. Missing coverage holds transport; a value or owner mismatch fails it.
6a. Equal Unknown, blank or null mapped values remain unresolved and hold transport. Valid Boolean false and permitted numeric zero are observed values. For HYBRID, inspect the instant_form and website transport and selected-source-origin branch records independently. A correct branch cannot pass an incomplete or wrong sibling branch.
7. Treat the Meta testing tool as fake/organic and not ad-associated. Endpoint success cannot prove paid source, CRM values, owner, deduplication, follow-up, qualification or CRM-to-Meta feedback.
8. Keep source-origin verification on Hold until a controlled submission through the selected real source path is read back in the destination.
9. Submitted, Delivered, Accepted, Contacted, Qualified and Booked require their own observed events. Unknown stays Unknown.
10. Reject invented claims, owner opinions, performance results or current account controls.
MISSING-INPUT BEHAVIOR
Return one stable request per missing field. Preserve valid supplied request IDs. Missing privacy approval, CRM destination or record owner must include these exact requests when applicable: `req-privacy-approval`, `req-crm-destination`, `req-record-owner`. Do not rename the field because a human label changes.
OUTPUT CONTRACT
Return:
- First, machine-readable JSON that is the complete canonical record at top level and can be replayed directly through the evaluator. Construct this record before the prose. Use contract_version `1.0.0`, a route enum string, the exact lowercase state keys and exact readiness values in the executable contract. Include checks, full request objects with exactly request_id/field/owner/action, next_test and limitations as additional top-level fields. Never output generic PASS/HOLD/FAIL in the six readiness state fields. Never add `consequence` or any fifth key to a canonical request object.
- Overall result: PASS, FAIL or HOLD.
- DRAFT, FORM, CRM, TRANSPORT, SOURCE_ORIGIN and LAUNCH states.
- Route decision and any contradiction.
- A human-readable identity and tracking section that repeats the supplied campaign ID, ad set ID, ad ID, Page ID, form ID and tracking specification verbatim.
- Message-continuity findings.
- Field, privacy and CRM findings.
- Delivery-receipt comparison.
- Open requests with request ID, field, owner, consequence and exact action. Consequence belongs only in this prose list or a separate `checks` item, never inside the canonical `requests` array.
- Smallest next action that can clear the first Hold.
The human-readable identity and tracking section is mandatory in every response, including Hold and Failed cases. Also place it in the top-level JSON field `human_readable_handoff` with `states` copied byte for byte from the canonical `states` object, `identity_and_tracking` containing campaign_id, ad_set_id, ad_id, page_id, form_id and tracking_specification copied verbatim, and eight named sections from `normalized_record_schema.human_readable_handoff.sections`. Print a visible `## Human-readable implementation handoff` section after the JSON with those same states and identity/tracking values. Do not omit this because the JSON already mentions them.
FINAL SELF-CHECK BEFORE RETURNING
- The complete `requests` array must include every unresolved canonical path and every derived field, transport and hybrid branch request. Distinct IDs with the same field are both required.
- Copy branch request actions byte for byte from the embedded contract. A supplied wrong source-test kind is FAILED with `-test-kind-mismatch`; an absent kind is HOLD with `-test-kind`.
- Sort the final `requests` array by the complete `request_id` string in ascending bytewise lexicographic order immediately before serializing JSON. Check every adjacent pair. For example, `req-failure-owner` comes before every `req-field-...` ID; never group requests by topic or owner.
- The visible human handoff and its JSON object must both be present. Missing or Failed evidence does not remove these sections.
HANDOFF AND RECEIPT TO REVIEW
Paste the complete files here.
Completed fictional handoff
The completed Facebook Lead Ads handoff contains the fictional pipeline-workbook example in one reviewable record. It includes the campaign, ad set, ad, Page and form identity; the tracking specification; route; message contract; form type; fields; qualification rules; privacy owner; ending; CRM map; deduplication rule; expected assignment; response target and test states.
Open it when you need to see how the decisions fit together. Review the route and message first, then check the form and CRM tables. The final section tells the implementer what remains on Hold.
This example is fictional. It does not describe a Maestro client campaign or a live CRM result.
Blank implementation handoff
Download the blank Facebook Lead Ads handoff before implementation.
- Fill in the route and promised next action.
- Complete the ad and form message contract.
- Add every field, question and answer option.
- Name the privacy and consent owner.
- Map every value into the destination.
- Define deduplication, assignment, response timing and failure handling.
- Give the completed file to the media buyer, CRM owner and reviewer.
If the privacy approval, CRM destination or owner is missing, leave the affected item on Hold and write the exact request needed to continue.
Delivery receipt
Use the completed fictional delivery receipt to see the expected evidence. It compares sent and received values and records the source type, lead ID, form ID, destination record ID, timestamps, timezone, latency, owner, duplicate result and failures.
Copy the blank delivery receipt for the real test. A reviewer should be able to decide whether transport passed without opening the connector settings.
Standalone build and review prompts
The Facebook Lead Ads build prompt turns supplied campaign inputs into a complete implementation handoff. Paste the prompt into a new chat and replace the input block. It requests missing facts instead of inventing them.
The Facebook Lead Ads review prompt checks an existing handoff and delivery receipt. Give it the files or paste their complete text. It returns the route decision, launch state, exact Holds and the evidence needed to clear each one.
Both prompts separate draft readiness from launch readiness. A missing final destination may allow a provisional draft. Missing privacy approval, CRM destination or owner blocks launch verification.
Final preflight
Before spend starts, require a yes or an explicit Hold for each line:
- The selected route fits the action after the click.
- The ad, form introduction and ending make the same promise.
- Every field and question has a defined use.
- Privacy, consent and disclaimer treatment has an owner and approval state.
- The destination URL and completion action were inspected.
- Every source value maps into a named destination field.
- Deduplication, assignment, response target and failure escalation are defined.
- The transport test passed with destination read-back.
- The source-origin test passed, or it remains visibly on Hold.
- Reporting keeps Submitted, Delivered, Accepted, Contacted, Qualified, Booked, Duplicate, Failed, Held and Unknown separate.
If the team finds a gap in the route, form, CRM map or test evidence, bring the redacted handoff and receipt to a free Demand Gen Roast. The review can start with the actual decisions and missing evidence instead of rebuilding the campaign from memory.
Sources and method
Platform mechanics were checked against current Meta for Business Help and Meta for Developers pages on 27 September 2026. The test-tool screenshots come from Meta's developer documentation, updated 10 September 2025. Form-builder screenshots come from a 2023 LeadsBridge walkthrough and are labeled wherever used; current labels were verified separately against Meta Help.
The five creative examples were visually inspected from stored public Meta ad records captured 23 September 2026. Their stored destinations are external websites. The analysis is limited to visible advertiser identity, offer, creative, headline, CTA and destination. Hidden fields, consent, CRM delivery, qualification and results remain Unknown.
Primary references: