Skip to main content
Glama

Metadata MCP Connector

Create or Update Offer

create_update_offer
Destructive
Create or update Lead Gen or Landing Page offers in Metadata platform.
Returns direct URL to access the offer in the platform.

OPERATION MODE:
- CREATE: When 'id' field is NOT included in data
- UPDATE: When 'id' field IS included in data

OFFER TYPES:
1. Lead Gen - Native forms rendered inside LinkedIn, Facebook, or Instagram. User never leaves the platform.
2. Landing Page - External page hosted on the advertiser's website. Works on EVERY channel (LinkedIn, Facebook, Instagram, Google Ads, Reddit, Microsoft Ads).

WARNING: LEAD GEN ON LINKEDIN / META CAN BE DONE TWO WAYS — pick the right one for the user's intent:
- Native Lead Gen form (offer_type='Lead Gen', data.channel=<channel>, goal=CPL): user fills the form INSIDE LinkedIn/Meta. Lower friction, higher conversion, but leads stay in the ad platform until synced. Channel-locked — see below.
- Landing Page lead capture (offer_type='Landing Page', goal=CPL, landingPageUrl + thankYouUrl + EMAIL field): user clicks through to the advertiser's own landing page and submits the form there. Choose this when the user wants branded landing pages, tracking pixels, or server-side enrichment. Channel-agnostic — one offer works on every channel.
Both flows count as Lead Gen (goal=CPL). Default to native Lead Gen for LinkedIn/Meta unless the user explicitly wants their own landing page.

WARNING: CHANNEL-LOCK ON NATIVE LEAD GEN OFFERS (read this BEFORE planning your offer batch)
The data.channel value you set on a Lead Gen offer PERMANENTLY locks the returned offer_id to that one channel. The same offer_id CANNOT be used in another channel's slot inside create_campaign / add_and_edit_campaign_elements — those tools accept the mismatch SILENTLY and produce a broken campaign that returns a successful response but cannot serve. Landing Page offers are NOT channel-locked and can be safely reused across channels.

What this means for planning:
- Want a native Lead Gen "Demo" concept on BOTH LinkedIn AND Facebook? You MUST call create_update_offer TWICE for that one concept — once with data.channel='LINKEDIN', once with data.channel='FACEBOOK'. Track both offer_ids and assign each to the matching channel slot.
- Three concepts × two native Lead Gen channels = SIX create_update_offer calls. That is correct, not over-creation. The "2-3 offers" guidance refers to distinct CONCEPTS, not call count.
- Want a single offer that works on every channel including Google? Use offer_type='Landing Page' (omit data.channel — Landing Pages have no channel field and are channel-agnostic). Google Ads ONLY accepts Landing Page offers.
- Want Lead Gen on LinkedIn using the advertiser's OWN landing page instead of LinkedIn's native form? Use offer_type='Landing Page' with goal='CPL' — then assign the returned offer_id to linkedin.offers[]. This is fully supported and a single LP offer_id can serve LinkedIn, Meta, Google simultaneously.

Right after each successful create_update_offer call, write down (offer_id, channel, concept) so you can correctly map them in the create_campaign call later. If you lose track, call get_offer(name=<offer_name>) to look up the offer and read back the channel field before submitting a campaign.

REQUIRED PARAMETERS:
- offer_type: "Lead Gen" or "Landing Page"
- data: Offer configuration object
  - id: (optional) Include only when updating an existing offer

IMPORTANT LIMITS:
- offerName: MUST be unique AND under 50 characters. If name + timestamp exceeds 49 chars, shorten the base name.
  WARNING: ALLOWED CHARACTERS: letters (a-z, A-Z), numbers (0-9), spaces, and these punctuation marks ONLY:
    _ . ? , : ' / + - = & ; % $ # [ ] | ( )
  Any other character (em-dashes, smart quotes, emoji, non-ASCII letters, ©, ™, !, @, *, {, }, <, >, ", \, ^, ~, `) will be REJECTED by the platform on Landing Page offers and stripped by this tool before the request is sent. Stick to ASCII letters/digits/space and the punctuation set above.
- offerHeader: max 60 chars
- offerDetails: max 160 chars
- thankYouMessage: max 60 chars (LinkedIn)
- privacyText: max 70 chars (Facebook/Instagram)
- Fields should always be included

CHANNEL-SPECIFIC REQUIREMENTS:

LinkedIn Lead Gen (offer_type="Lead Gen", channel="LINKEDIN"):
- REQUIRED: channel, offerName, offerHeader, thankYouMessage, thankYouUrl, privacyUrl, privacyText, fields
- CTA (linkedinCTA): REQUIRED by the backend. Defaults to DOWNLOAD_NOW when omitted. Must be one of
  LEARN_MORE | VIEW_NOW | DOWNLOAD_NOW | TRY_NOW | VISIT_COMPANY_WEBSITE. Any other value silently falls
  back to VISIT_COMPANY_WEBSITE on LinkedIn's side — always set linkedinCTA explicitly for predictable behavior.
- offerDetails: OPTIONAL but STRONGLY RECOMMENDED. Populates the description shown above the form on LinkedIn
  and improves conversion. Not mandatory on LinkedIn (unlike FB/IG) but there's no reason to skip it.

Facebook/Instagram Lead Gen (offer_type="Lead Gen", channel="FACEBOOK" or "INSTAGRAM"):
 - REQUIRED: channel, offerName, offerHeader, offerDetails, thankYouHeadline,
   thankYouMessage, thankYouDescription, thankYouPageButtonText, websiteUrl, privacyUrl
 - Meta's thank-you screen has THREE distinct text blocks — populate all three for a complete
   UX:
     • thankYouHeadline → title
     • thankYouMessage  → short_message (small text under the headline)
     • thankYouDescription → body (longer copy). Falls back to thankYouMessage if omitted,
       but supplying both is strongly preferred.
 - thankYouPageButtonType (enum, default VIEW_WEBSITE). Cross-field rules:
     • VIEW_WEBSITE → websiteUrl (or thankYouUrl fallback) is required
     • VIEW_ON_FACEBOOK → thankYouAssetId (library creative ID) is required; without it the
       backend validation will reject the offer.
 - CRITICAL: offerDetails is MANDATORY when offerHeader is provided. If the user doesn't
   specify offer details, generate a brief description based on the offer context.
   Omitting offerDetails when offerHeader is set will cause an API validation error.
 - The backend HTTP-probes followUpActionUrl (derived from websiteUrl / thankYouUrl) during
   validation, so the URL MUST resolve to a live page — not a placeholder.
 - linkedinCTA is ignored on FB/IG.

Landing Page (offer_type="Landing Page"):
- REQUIRED: offerName, landingPageUrl, thankYouUrl, fields
- CRITICAL: fields MUST include an EMAIL entry SPECIFICALLY. The platform's
  `INVALID_LANDINGPAGE_FORM: Email mapping is required` check is satisfied
  ONLY by EMAIL — BUSINESS_EMAIL alone does NOT satisfy it and the offer is
  rejected. Without an email capture, the form cannot deliver a lead. If the
  user does not mention form fields at all, default to:
    fields: [{"name": "email", "field": "EMAIL"}, {"name": "firstName", "field": "FIRST_NAME"}, {"name": "lastName", "field": "LAST_NAME"}, {"name": "company", "field": "COMPANY"}]
  BUSINESS_EMAIL may be added as an EXTRA field when the user explicitly wants
  to filter out personal email domains, but it can NEVER replace EMAIL — keep
  EMAIL in the form. (The server auto-corrects a lone BUSINESS_EMAIL to EMAIL.)

AVAILABLE FIELD TYPES BY OFFER TYPE:

For Lead Gen offers, the supported field types DIFFER BY CHANNEL — and the enum
SPELLINGS differ too (LinkedIn PHONE_NUMBER/ZIP_CODE vs Meta PHONE/ZIP). Mixing
them up is what causes the "Cannot deserialize Convo LeadForm JSON" attach failure.
PREFER the channel's DEFAULT-VISIBLE fields (marked [default] below) — they cover
the standard B2B form; only reach for the rest when the brief specifically needs them.

LinkedIn Lead Gen (channel="LINKEDIN") — these 22 types ONLY (source of truth:
the platform's LinkedIn Lead Gen Form dialog):
[default] "EMAIL", "WORK_EMAIL", "FIRST_NAME", "LAST_NAME", "COMPANY_NAME", "LINKEDIN_PROFILE_LINK"
[more]    "PHONE_NUMBER", "WORK_PHONE_NUMBER", "COUNTRY", "CITY", "STATE", "ZIP_CODE", "JOB_TITLE",
          "JOB_FUNCTION", "SENIORITY", "INDUSTRY", "COMPANY_SIZE", "DEGREE", "SCHOOL",
          "FIELD_OF_STUDY", "GRADUATION_DATE", "START_DATE"
LinkedIn has NO field for REVENUE, EMPLOYEES (use COMPANY_SIZE), GENDER, DOB, MESSENGER,
STREET_ADDRESS, etc. LinkedIn caps a form at 12 fields.

Facebook/Instagram Lead Gen (channel="FACEBOOK"/"INSTAGRAM") — these 19 types ONLY
(source of truth: the platform's FB/IG Lead Gen Form dialog):
[default] "EMAIL", "WORK_EMAIL", "FIRST_NAME", "LAST_NAME", "COMPANY_NAME", "COUNTRY"
[more]    "PHONE", "WORK_PHONE_NUMBER", "STREET_ADDRESS", "CITY", "STATE", "PROVINCE",
          "POST_CODE", "ZIP", "GENDER", "MARITIAL_STATUS", "RELATIONSHIP_STATUS",
          "MILITARY_STATUS", "JOB_TITLE"
Meta has NO field for REVENUE, EMPLOYEES, COMPANY_SIZE, SENIORITY, INDUSTRY, JOB_FUNCTION,
or the education fields (those are LinkedIn-only). "MARITIAL_STATUS" is the literal enum
(the typo is load-bearing — do not "fix" it). No field-count cap on Meta.
IMPORTANT: Do NOT use "BUSINESS_EMAIL" for Lead Gen - use "WORK_EMAIL" instead.
IMPORTANT: Do NOT use "COMPANY" for Lead Gen - use "COMPANY_NAME" instead.
IMPORTANT: Do NOT use "PHONE" for LinkedIn Lead Gen - use "PHONE_NUMBER" instead (auto-corrected if provided).
IMPORTANT: Do NOT use "PHONE_NUMBER" or "ZIP_CODE" for Facebook/Instagram - use "PHONE" / "ZIP" instead (auto-corrected if provided).
IMPORTANT: Custom / free-text questions are NOT supported. Every fields[].field MUST be one of the
predefined types for the offer's channel. Do NOT pass "CUSTOM", and do NOT invent a field for a concept
the brief names but that has no predefined type (e.g. "monthly transaction volume", "budget", "ad spend",
"use case", "team size"). Neither channel has a revenue/spend/budget field — DROP such a field. For
headcount: LinkedIn uses COMPANY_SIZE; Meta has no equivalent (drop). This server auto-normalizes near-miss
spellings (LinkedIn: EMPLOYEES->COMPANY_SIZE, ZIP->ZIP_CODE, POST_CODE->ZIP_CODE, PROVINCE->STATE; Meta:
PHONE_NUMBER->PHONE, ZIP_CODE->ZIP) and then DROPS any field the offer's channel still can't render
(logged), so the offer ships with its valid fields instead of failing at campaign-attach — but pass
channel-correct fields up front so nothing the user asked for is silently dropped.

For Landing Page offers (LandingPageFieldType):
"EMAIL", "BUSINESS_EMAIL", "FIRST_NAME", "LAST_NAME", "COUNTRY", "PHONE", "CITY", "STATE", "ZIP",
"JOB_TITLE", "SENIORITY", "COMPANY", "COMPANY_DOMAIN", "COMPANY_PHONE", "EMPLOYEES", "INDUSTRY",
"REVENUE", "LINKEDIN_URL", "UTM_SOURCE", "UTM_MEDIUM", "UTM_TERM", "UTM_CONTENT"
IMPORTANT: Do NOT use "COMPANY_NAME" for Landing Pages - use "COMPANY" instead.
IMPORTANT: Do NOT use "WORK_PHONE_NUMBER" for Landing Pages - use "PHONE" instead.

EXAMPLES:

CREATE LinkedIn Offer:
create_or_update_offer("Lead Gen", {
    "channel": "LINKEDIN",
    "status": "COMPLETED",
    "offerName": "Free AI Marketing Audit",
    "offerHeader": "Unlock Growth with AI",
    "offerDetails": "A 30-minute audit of your current AI stack with tailored recommendations.",
    "linkedinCTA": "DOWNLOAD_NOW",
    "thankYouMessage": "We'll contact you soon!",
    "thankYouUrl": "https://example.com/thanks",
    "privacyUrl": "https://example.com/privacy",
    "privacyText": "We respect your privacy",
    "fields": [
        {"name": "email", "field": "EMAIL"},
        {"name": "firstName", "field": "FIRST_NAME"},
    ]
})

UPDATE LinkedIn Offer:
create_or_update_offer("Lead Gen", {
    "id": 62148,
    "channel": "LINKEDIN",
    "status": "COMPLETED",
    "offerName": "Free AI Marketing Audit 2025",
    "offerHeader": "Unlock Growth with AI",
    "offerDetails": "A 30-minute audit of your current AI stack with tailored recommendations.",
    "linkedinCTA": "LEARN_MORE",
    "thankYouMessage": "We'll contact you soon!",
    "thankYouUrl": "https://example.com/thanks",
    "privacyUrl": "https://example.com/privacy",
    "privacyText": "We respect your privacy",
    "fields": [
        {"name": "email", "field": "EMAIL"},
        {"name": "firstName", "field": "FIRST_NAME"},
    ]
})

CREATE Facebook/Instagram Offer:
create_or_update_offer("Lead Gen", {
    "channel": "FACEBOOK",
    "status": "COMPLETED",
    "offerName": "Free Consultation",
    "offerHeader": "Get Expert Advice",
    "offerDetails": "Book a 30-minute consultation with our senior strategists.",
    "thankYouHeadline": "Thank You!",
    "thankYouMessage": "We'll be in touch soon",
    "thankYouDescription": "A member of our team will email you within one business day to schedule your session.",
    "thankYouPageButtonText": "Visit Website",
    "thankYouPageButtonType": "VIEW_WEBSITE",
    "websiteUrl": "https://example.com",
    "privacyUrl": "https://example.com/privacy",
    "privacyText": "Privacy Policy",
    "fields": [
        {"name": "email", "field": "EMAIL"},
        {"name": "firstName", "field": "FIRST_NAME"},
    ]
})

CREATE Landing Page Offer:
create_or_update_offer("Landing Page", {
    "offerName": "Free Demo",
    "landingPageUrl": "https://example.com/landing",
    "thankYouUrl": "https://example.com/thanks",
    "fields": [
        {"name": "email", "field": "EMAIL"},
        {"name": "firstName", "field": "FIRST_NAME"},
    ]
})

RESPONSE: Success returns offer_id, offer_url, and configuration details.
VALIDATION: Checks URLs start with https://, character limits, required fields per channel.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dataYesOffer configuration data. Required fields depend on offer_type and channel - see description for details.
offer_typeYesType of offer to create

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only signal mutation (readOnlyHint=false, destructiveHint=true), but the description adds substantial behavioral disclosures: permanent channel lock, silent fallbacks (linkedinCTA→VISIT_COMPANY_WEBSITE), server auto-correction of near-miss field spellings, silent dropping of unsupported fields, auto-stripping of invalid characters, and HTTP-probing of thankYouUrl during validation. These go far beyond the annotations and are critical for correct use.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded and well-structured with clear headings, warnings, and examples. However, it is very long and contains some redundancy with the schema (e.g., allowed-character list appears in both places, and several IMPORTANT warnings are repeated). Still, each section carries necessary operational detail for a high-stakes, channel-sensitive tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description covers return values ('Success returns offer_id, offer_url, and configuration details'). It also specifies validation behavior (URLs must start with https://, character limits, per-channel rules) and gives examples for every major path (LinkedIn, FB/IG, Landing Page, update). Nothing an agent needs to call this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Even though schema description coverage is 100%, the description adds extensive parameter semantics: allowed character sets for offerName, channel-specific required fields, the full per-channel field-type enums, defaults (linkedinCTA, status, thankYouPageButtonType), maximum lengths, and the 'EMAIL required for Landing Pages' rule. It also clarifies that 'MARITIAL_STATUS' is the literal enum and must not be 'fixed'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states exactly what the tool does: 'Create or update Lead Gen or Landing Page offers in Metadata platform.' It names a specific verb (create/update), a specific resource (offers), and the two offer types. The description further distinguishes it from siblings by explaining how it fits into campaign creation (mapping offer_id to create_campaign) and when to use get_offer as a lookup fallback.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit guidance on when to use which path: CREATE vs UPDATE based on presence of 'id', native Lead Gen vs Landing Page based on user intent, and channel-lock-driven call planning (e.g., 'You MUST call create_update_offer TWICE for that one concept'). It also tells the agent what to do after success ('write down (offer_id, channel, concept)') and points to get_offer as an alternative when tracking is lost.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources