Features Add-ons Pricing Cost calculator Use cases Compare Blog

Button at Index 0 of Type Url Requires a Parameter: 131008

14 min read

The short answer

WhatsApp Cloud API error 131008 with the detail "Button at index 0 of type Url requires a parameter" means the template's first button is a URL ending in a variable and the send carried no value for it. Add a button component with sub_type url, that button's zero-based index, and only the percent-encoded suffix (checked October 2026).

Campaigns list in the Acme Retail demo workspace showing three campaigns on the festive_offer template, one scheduled, one sending at 62 percent with Pause and Cancel buttons, and one completed with 1,792 sent and 1,394 read, beside a green New campaign button.

What does "Button at index 0 of type Url requires a parameter" mean?

It means the template you sent has a URL button whose link ends in a variable, and your send request carried no value for that variable. The WhatsApp Cloud API refuses the message with error 131008, and the details field names the button by its position: index 0 is the first button on the template.

Meta's own error reference describes 131008 in one generic line, written for every endpoint at once:

"The request is missing a required parameter." (Meta — WhatsApp error codes)

Its suggested fix is just as general: "See the endpoint’s reference to determine which parameters are required." Nothing on that page mentions buttons. I searched it on 17 October 2026 for "button", "requires a parameter" and "index 0 of type", and found none of them. The sentence in your logs comes from the API's response, not from Meta's documentation, which is why searching the documentation for it returns nothing useful.

The shape of the failure is nearly always the same. The template was approved with a link such as https://example.com/track?id={{1}}. The body variables were filled correctly, so the request looks complete to anyone reading it. But a dynamic link is a separate kind of value from header and body parameters, and it travels in a component of its own. Leave that component out and the template is missing a parameter, even though every placeholder you can see in the message text has a value.

Five ways a WhatsApp dynamic URL button send can end, from a missing button component that returns error 131008, through a wrong index, a whole address and an unencoded value, to the one shape Meta accepts
Only the last rung is a complete send. The first is the one that produces this exact error text.

Where does the button wording come from if Meta's error page never prints it?

It comes from the details property of the error object, which is the field Meta tells you to build on. The error reference is explicit about which properties deserve your logic:

"In general, Meta recommends that you build your app’s error handling logic around code values and details payload properties. These properties and their values are more indicative of the underlying error." (Meta — WhatsApp error codes)

Meta defines details as an "Error description and a description of the most likely reason for the error. May also contain information on how to address the error, such as which parameter is invalid or what values are acceptable." For this failure, that is where the button wording sits. The message field is something else: a "Combination of the error code and its title", and Meta warns that "titles will eventually be deprecated", so nothing should parse them.

Put together, the response has this shape. The envelope is Meta's documented error syntax; the details string is the text the API returned for this failure in a refused send, not wording from any Meta page:

{
  "error": {
    "message": "(#131008) <TITLE>",
    "type": "<TYPE>",
    "code": 131008,
    "error_data": {
      "messaging_product": "whatsapp",
      "details": "Button at index 0 of type Url requires a parameter"
    },
    "fbtrace_id": "<FBTRACE_ID>"
  }
}

Two practical consequences follow. First, match on code 131008 and read the index out of details, rather than matching the whole sentence, because the index changes with the template. Second, watch both delivery routes. Meta says "Cloud API errors are returned either synchronously as a Graph API response, asynchronously via Webhook, or sometimes through both methods", and the page does not say which route 131008 takes. If you send from a queue, the refusal appears whenever the job runs, which can be minutes after someone pressed Send.

What does a correct dynamic URL button send look like?

A correct send carries one button component per dynamic link, with sub_type set to url, an index matching the button's position, and a single text parameter holding the value. Meta's limited-time offer reference shows the pairing end to end. The template is created with the variable at the very end of the address:

{
  "type": "url",
  "text": "Book now!",
  "url": "https://awesomedestinations.com/offers?code={{1}}",
  "example": ["https://awesomedestinations.com/offers?ref=n3mtql"]
}

And the send fills it like this, beside the body parameters, with the copy-code button sitting at index 0 and the link at index 1:

{
  "type": "button",
  "sub_type": "url",
  "index": 1,
  "parameters": [
    { "type": "text", "text": "n3mtql" }
  ]
}

Both snippets are taken from Meta's limited-time offer templates page. Notice the send component's type is button, singular, one per button that needs a value, while at creation every button sits together inside a single buttons component. Also notice that the body parameters and this button component are siblings in the same components array. Teams that build the send from a helper written for body variables tend to produce a perfect body array and no button entry at all, which is exactly the request that earns this error.

Body variables have their own failure modes, covered in why error 132000 fires with the right number of parameters. A send can have every body parameter right and still be missing the button value, which is why the two errors are worth telling apart.

Which index does a URL button have when the template has other buttons?

Its zero-based position among all of the template's buttons, counted in the order they were defined, whatever their type. A quick reply or a copy-code button in front of the link pushes the link's index up by one. Meta's coupon reference defines the field like this:

"Indicates the order in which a button appears, if the template uses multiple buttons. Buttons are zero-indexed, so setting the value to 0 causes the button to appear first, and another button with an index of 1 appears next, and so on." (Meta — Coupon code templates)

Meta's own examples confirm that every button counts. The coupon template is created with a quick reply first and a copy-code button second, and the send addresses the copy-code button at index 1. The limited-time offer reference states the rule for its link outright: "URL button index. If the template uses a copy code button, value must be 1." And the media card carousel reference walks through the exact mistake:

"For example, if the template defines a phone number button first (which equates to index 0) and a URL button that supports a single variable second (which equates to index 1), if you attempt to send the template with the URL button index set to 0 , the API would return an error (“Parameter value for URL was expected but was not found”) because it’s expecting a button object with an index of 1 to be present in the post body payload." (Meta — Media card carousel templates)

That example is the useful one, because it shows the wrong index does not produce a friendly "index mismatch". Meta reads it as the button at the expected position having no value, so an index mistake reads like a forgotten component. Meta's example gives the wording but not the error code, so do not assume it matches the details string above word for word.

Buttons on the template, in order Index of the URL button Source of the rule
URL 0 Zero-based counting
Copy code, URL 1 Limited-time offer reference
Phone number, URL 1 Carousel reference example
Quick reply, quick reply, URL 2 Zero-based counting
URL, URL 0 and 1 Two components, one each

One more wrinkle. The components reference says that for a template with several quick replies, "you can use the index property to designate the order in which buttons appear in the template message." The carousel reference closes that door as soon as a variable is involved: "if any buttons use variables, the type and order of buttons must match the type and order defined on the template". For a dynamic link, treat the index as a fixed address, never as a sort key.

Do you send the whole link or only the part that changes?

Only the part that changes. The template components reference describes the button URL as one that "Supports 1 variable, appended to the end of the URL string." The value you send is appended to the base the template already holds. Send the whole address and the link the customer receives contains the base twice, and the https:// inside the value brings a colon, which is one of the characters Meta lists as needing encoding.

The length rule follows from the same design. On a limited-time offer, Meta describes the URL variable as having "No maximum, but value counts against URL string maximum of 2000 characters." The components reference gives the same "2000 characters maximum" for a URL button in general, so a long tracking token is fine until base plus value passes 2,000 characters.

The creation side is where people get confused, because Meta's examples disagree with each other. The components reference's property table gives the example as a finished Lucky Shrub shop address ending in ?promo=summer2023, while the JSON sample directly beneath it gives only summer2023. The limited-time offer sample uses a full address. Both forms appear in Meta's own documentation for template creation. The send side has no such ambiguity: the parameter is only the suffix in every send example on the pages cited here.

Why does a value with spaces, colons or accents break the link?

Because it is substituted into an address, and Meta requires URL-safe characters there. The components reference says so directly:

"If your URL button parameter values contain special characters, you must percent-encode them before including them in your send template message request. Unencoded special characters can cause the generated URL to fail validation, resulting in a message send error." (Meta — Template components)

Meta lists the characters that are, in its words, "common sources of encoding issues":

Character Encoded value Meta's example
Space %20 New York → New%20York
: %3A x:key → x%3Akey
| %7C 9|DL → 9%7CDL
ç %C3%A7 Gonçalves → Gon%C3%A7alves
ñ %C3%B1 Peña → Pe%C3%B1a

For senders in India the same rule catches city names (New Delhi becomes New%20Delhi), order references built with pipes (ORD|2291 becomes ORD%7C2291), and any name in Devanagari, where each character becomes several encoded bytes (राम becomes %E0%A4%B0%E0%A4%BE%E0%A4%AE). Use your language's component encoder: encodeURIComponent in JavaScript, rawurlencode in PHP (not urlencode, which writes a space as + rather than Meta's %20), or quote(value, safe='') in Python.

Encode exactly once. A value that is already encoded and gets encoded again turns %20 into %2520, and the customer lands on a page looking for a reference that does not exist. This is a different failure from 131008: the value is present, so nothing is missing, and the problem shows only when someone taps the button.

One caution about scope. The encoding rule is written about URL button values that become part of a link you defined. Meta's copy-code authentication example sends the code J$FpnYnP on a url sub-type button without encoding the dollar sign. Meta does not reconcile the two pages, so follow the encoding rule for marketing and utility links, and Meta's authentication example for one-time codes.

Which other buttons need a value at send time?

Three kinds of button carry a send-time value, and each one needs its own component. Everything else is fixed at approval and is simply left out of the send.

Button on the template Value at send time? What the component carries
URL ending in a variable Yes sub_type url, one text parameter
URL with no variable No Nothing; omit it
Copy code Yes sub_type copy_code, a coupon_code
Authentication code button Yes, the code sub_type url, the code as text
Quick reply No Omitted in Meta's coupon send

The copy-code row comes from the components reference, which says the string to copy is "defined when the template is sent in a template message". The authentication row is the one that surprises people. Meta describes one-time password buttons as "a special type of URL button component used with authentication templates", and its copy-code authentication example sends the code on a button whose sub_type is url, with a note: "this value must appear twice in the payload." Once in the body, once on the button. Meta does not say what the API answers if the code appears only once, so send it in both places, as the example does.

Why does the delivered link have a slash you never typed?

That one is not 131008, and none of the Meta pages cited here describes it. One developer on Meta's community forum reported, in a thread the forum showed as about two years old when this was written, that a template URL ending in ?reference={{1}}, created in Meta's web interface, was saved after submission as ?reference=/{{1}}, so every delivered link carried a stray slash before the value. A second user later asked whether anyone had found a solution, and the thread on Meta's developer forum is marked unresolved. It is one user's report, not a documented rule.

The check is cheap either way. Read the approved template back from the API and compare the stored url with what you typed. If the stored address differs, nothing you change in the send request will fix it, because the value is appended to whatever the template holds. The template itself has to be corrected and approved again.

How do you catch a missing button value before Meta does?

Read the approved template, walk its buttons in order, and refuse to send until every button that needs a value has one. In outline:

const buttons = template.components
  .find((c) => c.type === 'BUTTONS')?.buttons ?? [];

buttons.forEach((button, index) => {
  const dynamicUrl = button.type === 'URL' && button.url.includes('{{');
  if ((dynamicUrl || button.type === 'COPY_CODE') && !values[index]) {
    throw new Error(`Button ${index} (${button.type}) needs a value`);
  }
});

The position comes from the loop, not from a count of URL buttons, which is what keeps a link behind a quick reply at index 1. Run this once per template, before a campaign starts, rather than per recipient. A missing button value lives in the payload every recipient shares, so a campaign that omits it fails every single send in the same way. The first refusal is the signal to stop, not something to retry.

The value is easy to miss for a reason worth knowing. A template list that counts variables from the message text will not count it, because a link's {{1}} is not in the text. WabaCRM's templates screen is an example: its Variables column adds the header and body placeholders, and a URL button's variable is not part of that figure.

Templates list in the Acme Retail demo workspace showing four templates with their message text, category, language, a Variables count and Pending, Approved or Rejected status badges beside Preview buttons
The Variables column counts header and body placeholders. A link's variable is collected separately at send time.

So WabaCRM collects it separately. When a template has a URL button ending in a variable, the inbox composer and the campaign builder each show a "Link value" field for that button and percent-encode what you type before it reaches Meta, so you type New Delhi rather than New%20Delhi (typing the encoded form would encode it twice). The inbox composer refuses to send while that field is empty. The REST API takes the same values in button_params, keyed by button index, and refuses a send that leaves one out, naming the index, rather than letting Meta answer with 131008. If you are sending to a contact your system has never messaged, sending a template to a new number through the API covers the rest of that request. The features page lists where templates are used across the product.

Every Meta quotation on this page was read from Meta's own documentation on 17 October 2026. Meta changes that documentation without notice; the linked pages are authoritative and this one is not.

Questions people also ask

Can one WhatsApp template carry two dynamic URL buttons?

Yes. Meta's template components reference says templates are limited to two URL buttons, and each address can carry one variable at its end. If both buttons are dynamic, the send needs two separate button components, each with sub_type url, its own index and its own text parameter. Supplying a value for only one of them leaves the other button without its parameter, which is exactly the condition this error describes, so expect the details text to name the index of the one you left out. Count that index across every button on the template, not only across the URL buttons.

Can the variable in a WhatsApp URL button sit in the middle of the address?

No. Meta's reference says a button URL supports one variable, appended to the end of the URL string, so the placeholder can only ever be the last thing in the address. If the part that changes sits in the middle of your link today, restructure the link so the changing part comes last, for example as the final path segment or the closing query parameter, and let your own server route it from there. Meta offers neither a second variable nor a position anywhere except the end.

Should the button index be sent as a string or a number?

Meta's own pages do both. The template components reference and the copy-code authentication example send the index as a quoted string, "0", while the coupon and limited-time offer references document it as an Integer and send 1 without quotes. None of those four pages says either form is refused. What matters is the value: the button's zero-based position among all the template's buttons, in the order they were defined. Pick one form, use it everywhere in your codebase, and spend your checking effort on the position rather than the quotes.

Can a URL button use a named parameter instead of a numbered one?

Yes. Meta's template components reference shows a button address of https://example.com/order?name={{customer_name}} and a matching send in which the button parameter carries parameter_name set to customer_name beside the encoded text value. So on a template created with named parameters, the button's parameter carries the same name the template declares, just as body parameters do. The value rules do not change with the naming style: it is still only the part that replaces the placeholder, still percent-encoded, and still sent at the button's own index.

Does a URL button without a variable need anything in the send request?

No. A static link is fixed when the template is approved, so there is nothing to fill in at send time and no button component to include for it. Meta's limited-time offer reference marks the URL variable as required only if the URL uses a variable, and Meta's coupon example sends a template that contains a quick reply button without any component for that button. A button component is required only where the button carries a send-time value: a URL ending in a variable, a copy-code button, or the code button on an authentication template.

Keep reading

Your customers are already on WhatsApp

₹999 once for 1,000 contacts — no monthly fee and no expiry. Signing up is free and needs no card, so you can connect your number and read what arrives before you pay. Connecting takes as long as Meta's own review of it.

Sign up with your company email address. No sales call, no onboarding fee, nothing to schedule.

Why this is safe to point your customer list at

Payments are processed by Razorpay on their own checkout — your card details are never entered on, or stored by, WabaCRM. Every inbound WhatsApp webhook is checked against its signature before it is trusted.

Tech Provider is a Meta platform access tier — not a partnership, a reseller agreement or an endorsement.