📄 Templates#

In the realm of WhatsApp messaging, templates serve as pre-approved message structures that businesses can use to initiate conversations with users. These templates are essential for sending notifications, updates, or any message that requires prior approval from WhatsApp.

Think of templates as reusable message blueprints that ensure your communications are consistent, compliant, and ready to engage your audience. They can include various components such as headers, bodies, footers, and buttons, allowing for rich and interactive messages.

PyWa offers a comprehensive and intuitive interface to create, manage, and send these templates seamlessly. Whether you’re looking to send promotional offers, account updates, or authentication codes, PyWa’s templating system ensures your messages are structured, consistent, and compliant with WhatsApp’s guidelines.

  • From developers.facebook.com:

    Templates are WhatsApp Business Account assets that can be sent in template messages via Cloud API or Marketing Messages Lite API. Template messages are the only type of message that can be sent to WhatsApp users outside of a customer service window, so templates are commonly used when messaging users in bulk, or when you need to message a user, but no customer service window is open between you and the user.

Defining Template#

To create a template, you need to define its structure using the Template class from the pywa.types.templates module. This involves specifying the template’s name, language, category, parameter format, and the components that make up the template.

In the example below, we create a simple Order Confirmation template with a header, body, footer, and buttons.

order_confirmation_template.py#
 1from pywa.types.templates import *
 2
 3order_confirmation = Template(
 4    name="order_confirmation",
 5    language=TemplateLanguage.ENGLISH_US,
 6    category=TemplateCategory.MARKETING,
 7    parameter_format=ParamFormat.NAMED,
 8    components=[
 9        HeaderText(text="Order Confirmation"),
10        BodyText(
11            "Hi {{name}}, your order {{order_id}} has been confirmed and will be delivered by {{delivery_date}}.",
12            name="John Doe",
13            order_id=12345,
14            delivery_date="August 5, 2025",
15        ),
16        Buttons(
17            buttons=[
18                QuickReplyButton(
19                    text="Contact Support",
20                ),
21                URLButton(
22                    text="Track Order",
23                    url="https://www.example.com/track-order?order_id={{1}}",
24                    example="12345",
25                ),
26            ]
27        ),
28        FooterText(text="Powered by Pywa"),
29    ],
30)

In this example, we define a template named “order_confirmation” in English (US) for marketing purposes. We use a named parameter format, allowing us to use descriptive names for our parameters like name, order_id, and delivery_date in the body text. The template includes a header with text, a body with dynamic parameters, buttons for quick replies and URLs, and a footer with additional information.

../../_static/examples/order-confirmation-template.png

This is how the template will look in WhatsApp once sent.#

Template Components#

Creating Template#

After defining your template, you can create it using the create_template() method:

create_template.py#
1from pywa import WhatsApp
2
3wa = WhatsApp(waba_id=...)
4
5wa.create_template(order_confirmation)
6# CreatedTemplate(id='...', category=TemplateCategory.MARKETING, status=TemplateStatus.PENDING)

After creating a template, you need to wait for WhatsApp to approve it. This process can take some time, and you can check the status of your template using the get_template() method or by checking the WhatsApp Manager Dashboard Manage templates.

See also

You can also use the get_templates() method to retrieve a list of all templates associated with your business account.

You can also wait_until_approved() for the template to be approved, which will block the execution until the template is approved or rejected.

wait_until_approved.py#
1from pywa import WhatsApp
2
3wa = WhatsApp(waba_id=...)
4
5template = wa.create_template(order_confirmation)
6template.wait_until_approved()

Or you can handle the template status using the on_template_status_update() decorator, which allows you to define a callback function that will be called whenever the template status changes:

on_template_status_update.py#
 1from pywa import WhatsApp, types
 2from pywa.types import TemplateStatusUpdate
 3from pywa.types.templates import TemplateStatus
 4
 5wa = WhatsApp(waba_id=..., token=...)
 6
 7@wa.on_template_status_update
 8def handle_template_status_update(_: WhatsApp, update: TemplateStatusUpdate):
 9    if update.new_status == TemplateStatus.APPROVED:
10        print(f"Template {update.template_id} approved!")
11    elif update.new_status == TemplateStatus.REJECTED:
12        print(f"Template {update.template_id} rejected: {update.reason}")

Sending Template#

Once your template is approved, you can send it using the send_template() method.

send_template.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(phone_id=..., token=...)
 5
 6wa.send_template(
 7    to="972123456789",
 8    name="order_confirmation",
 9    language=TemplateLanguage.ENGLISH_US,
10    params=[...]
11)

Now, the params list should contain the values for the components defined in the template.

The best practice is to use the Template object you created earlier as a reference for the parameters, ensuring that the values match the expected format:

send_template_with_object.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4order_confirmation = Template(
 5    name="order_confirmation",
 6    language=TemplateLanguage.ENGLISH_US,
 7    category=TemplateCategory.MARKETING,
 8    parameter_format=ParamFormat.NAMED,
 9    components=[
10        HeaderText(text="Order Confirmation"),
11        bdy := BodyText(
12            "Hi {{name}}, your order {{order_id}} has been confirmed and will be delivered by {{delivery_date}}.",
13            name="John Doe",
14            order_id=12345,
15            delivery_date="August 5, 2025",
16        ),
17        Buttons(
18            buttons=[
19                qrb := QuickReplyButton(
20                    text="Contact Support",
21                ),
22                urlb := URLButton(
23                    text="Track Order",
24                    url="https://www.example.com/track-order?order_id={{1}}",
25                    example="12345",
26                ),
27            ]
28        ),
29        FooterText(text="Powered by Pywa"),
30    ],
31)
32
33wa = WhatsApp(phone_id=..., token=...)
34
35wa.send_template(
36    to="972123456789",
37    name=order_confirmation.name,
38    language=order_confirmation.language,
39    params=[
40        bdy.params(name="Jane Doe", order_id=67890, delivery_date=DateTime(fallback_value="September 10, 2025")),
41        qrb.params(callback_data="contact-support", index=0),
42        urlb.params(url_variable="67890", index=1),
43    ],
44)

As you can see, we use the params method of each component to generate the parameters needed for the template.

Tip

Using params on the instance ensures that the parameters are correctly formatted and match the expected types, reducing the risk of errors when sending the template. But sometimes, you don’t have access to the template components instances (e.g when creating using WhatsApp Manager Dashboard), and you need to create the parameters manually. In that case, you can still use the params method on each component to create the parameters directly, as shown below:

send_template_without_object.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(phone_id=..., token=...)
 5
 6wa.send_template(
 7    to="972123456789",
 8    name="order_confirmation",
 9    language=TemplateLanguage.ENGLISH_US,
10    params=[
11        BodyText.params(name="Jane Doe", order_id=67890, delivery_date=DateTime(fallback_value="September 10, 2025")),
12        QuickReplyButton.params(callback_data="contact-support", index=0),
13        URLButton.params(url_variable="67890", index=1),
14    ],
15)

Media Templates#

When creating templates that include media, such as images, videos, or documents, you need to provide example of the media in the template definition. This is done using the example parameter in the media components.

Note

Media examples automatically uploaded via the Upload Resumable API, this requires to provide app_id when initializing the WhatsApp client. If you don’t provide the app_id, you will need to upload the media manually using the Upload Resumable API before creating the template and provide the file handle in the example parameter.

media_template.py#
 1from pywa.types.templates import *
 2
 3media_template = Template(
 4    name="media_template",
 5    language=TemplateLanguage.ENGLISH_US,
 6    category=TemplateCategory.MARKETING,
 7    components=[
 8        HeaderImage(example="https://www.example.com/image-example.jpg"),
 9        BodyText(text="Check out our new product!"),
10        FooterText(text="Visit our website for more details."),
11    ],
12)

The example parameter can be a URL, file path, Media or bytes. This example media will be used by WhatsApp to verify the template and ensure it meets their guidelines.

Tip

PyWa uploads the media automatically when creating the template and caches the file handle (from Upload Resumable API) in the header object. This means that if you create multiple templates with the same media example - you may want to use the same media object to avoid uploading the same media multiple times. This will save you time and resources.

media_template_with_cache.py#
 1from pywa import WhatsApp
 2from pywa.types.media import Media
 3from pywa.types.templates import *
 4
 5wa = WhatsApp(token=..., waba_id=..., app_id=...)
 6
 7image = pathlib.Path("path/to/image.jpg")
 8header_image = HeaderImage(example=image)
 9
10for lang in [TemplateLanguage.ENGLISH, TemplateLanguage.ENGLISH_US, TemplateLanguage.ENGLISH_UK]:
11    media_template = Template(
12        name="media_template",
13        language=lang,
14        category=TemplateCategory.MARKETING,
15        components=[
16            header_image,  # Reuse the same header image
17            BodyText(text="Check out our new product!"),
18            FooterText(text="Visit our website for more details."),
19        ],
20    )
21
22    wa.create_template(media_template)

The same goes for other media components (HeaderVideo and HeaderDocument), you can reuse the same media object across multiple templates to avoid uploading the same media multiple times.

When sending a media template, you can use the same approach as before, but now you need to provide the media as an parameter in the template:

send_media_template.py#
 1media_template = Template(
 2    name="media_template",
 3    language=TemplateLanguage.ENGLISH_US,
 4    category=TemplateCategory.MARKETING,
 5    components=[
 6        hi := HeaderImage(example="https://www.example.com/image-example.jpg"),
 7        BodyText(text="Check out our new product!"),
 8        FooterText(text="Visit our website for more details."),
 9    ],
10)
11
12wa.send_template(
13    to="972123456789",
14    name=media_template.name,
15    language=media_template.language,
16    params=[
17        hi.params(image="https://www.my-cdn.com/image.jpg"),
18    ],
19)

Tip

If you are sending the same template multiple times, you can cache the media object and reuse it across multiple template sends. This will save you time and resources, as the media will not be uploaded again.

send_media_template_with_cache.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(token=..., waba_id=..., app_id=...)
 5
 6media_template = Template(
 7    name="media_template",
 8    language=TemplateLanguage.ENGLISH_US,
 9    category=TemplateCategory.MARKETING,
10    components=[
11        hi := HeaderImage(example="https://www.example.com/image-example.jpg"),
12        BodyText(text="Check out our new product!"),
13        FooterText(text="Visit our website for more details."),
14    ],
15)
16
17hi_param = hi.params(image=pathlib.Path("path/to/image.jpg"))
18
19for phone_number in ["972123456789", "972987654321", "972456789123"]:
20    wa.send_template(
21        to=phone_number,
22        name=media_template.name,
23        language=media_template.language,
24        params=[hi_param],  # Reuse the same header image parameter
25    )

Authentication Templates#

Authentication templates are a special type of template used for sending authentication codes to users. If you need to send OTPs (One-Time Passwords) to users so they can verify their identity or complete a transaction in another app, you can use authentication templates.

Creating an authentication template is similar to creating a regular template, but it includes specific components for authentication, such as the AuthenticationBody and AuthenticationFooter.

authentication_template.py#
 1from pywa.types.templates import *
 2
 3auth_template = Template(
 4    name="auth_code",
 5    language=TemplateLanguage.ENGLISH_US,
 6    category=TemplateCategory.AUTHENTICATION,
 7    components=[
 8        AuthenticationBody(add_security_recommendation=True),
 9        AuthenticationFooter(code_expiration_minutes=5),
10        Buttons(
11            buttons=[
12                # An OTP Button
13            ],
14        ),
15    ],
16)

The OTP button can be one of the following types:

  • OneTapOTPButton: A button that allows the user to tap and automatically fill in the OTP code in the app:

    one_tap_otp_button.py#
     1from pywa.types.templates import *
     2
     3otp_button = OneTapOTPButton(
     4    text="Autofill Code",
     5    autofill_text="Autofill",
     6    supported_apps=[
     7        OTPSupportedApp(
     8            package_name="com.example.myapp",
     9            signature_hash="12345678901"
    10        ),
    11    ],
    12)
    
../../_static/examples/one-tap-auth-template.webp

  • ZeroTapOTPButton: A button that allows the user to receive the OTP code without any interaction.

    zero_tap_otp_button.py#
     1from pywa.types.templates import *
     2
     3otp_button = ZeroTapOTPButton(
     4    text="Autofill Code",
     5    autofill_text="Autofill",
     6    zero_tap_terms_accepted=5,
     7    supported_apps=[
     8        OTPSupportedApp(
     9            package_name="com.example.myapp",
    10            signature_hash="12345678901"
    11        ),
    12    ],
    13)
    
../../_static/examples/zero-tap-auth-template.png

  • CopyCodeOTPButton: A button that allows the user to copy the OTP code to the clipboard and use it in another app.

    copy_code_otp_button.py#
    1from pywa.types.templates import *
    2
    3otp_button = CopyCodeOTPButton()
    
../../_static/examples/copy-code-auth-template.webp

When sending an authentication template you need to provide the OTP code as a parameter to the AuthenticationBody and to the OTP button you are using.

send_authentication_template.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(phone_id=..., token=...)
 5
 6wa.send_template(
 7    to="972123456789",
 8    name="auth_code",
 9    language=TemplateLanguage.ENGLISH_US,
10    params=[
11        AuthenticationBody.params(otp="123456"),
12        OneTapOTPButton.params(otp="123456"),
13    ],
14)

Tip

Authentication templates texts are fixed and cannot be edited (except the button title), So WhatsApp allows you to create and update multiple authentication templates with the same name. This means you can create multiple authentication templates with the same name, but for different languages.

To create an authentication template for multiple languages, you can use the upsert_authentication_template() method, which allows you to create or update an authentication template for multiple languages at once.

create_authentication_template_multiple_languages.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(waba_id=..., token=...)
 5
 6templates = wa.upsert_authentication_template(
 7    name='one_tap_authentication',
 8    languages=[TemplateLanguage.ENGLISH_US, TemplateLanguage.FRENCH, TemplateLanguage.SPANISH],
 9    otp_button=OneTapOTPButton(supported_apps=...),
10    add_security_recommendation=True,
11    code_expiration_minutes=5,
12)
13for template in templates:
14    print(f'Template {template.id} created with status {template.status}')

Template Library#

PyWa also provides an easy way to create templates from the Template Library. The library contains a collection of pre-defined templates that you can use to quickly create and send messages without having to define them from scratch.

From developers.facebook.com:

Template Library

Template Library makes it faster and easier for businesses to create utility templates for common use cases, like payment reminders, delivery updates — and authentication templates for common identity verification use cases.

These pre-written templates have already been categorized as utility or authentication. Library templates contain fixed content that cannot be edited and parameters you can adapt for business or user-specific information.

You can browse and create templates using Template Library in WhatsApp Manager, or programmatically via the API.

To create a template from the library, you need to get the template library name and pass it to the create_template() method:

create_template_from_library.py#
 1from pywa import WhatsApp
 2
 3wa = WhatsApp(waba_id=..., token=...)
 4
 5# Define the template
 6order_update = LibraryTemplate(
 7    name="order_update",
 8    library_template_name="order_update_no_cta_1",
 9    category=TemplateCategory.UTILITY,
10    language=TemplateLanguage.ENGLISH_US,
11)
12
13# Create the template
14wa.create_template(order_update)
15# CreatedTemplate(id='...', category=TemplateCategory.UTILITY, status=TemplateStatus.PENDING)

If the library template requires parameters, you need to provide them when creating the template. The parameters should match the ones defined in the library template:

create_template_from_library_with_params.py#
 1from pywa import WhatsApp
 2from pywa.types.templates import *
 3
 4wa = WhatsApp(waba_id=..., token=...)
 5
 6# Define the template with parameters
 7order_update = LibraryTemplate(
 8    name="order_update",
 9    library_template_name="order_update_1",
10    category=TemplateCategory.UTILITY,
11    language=TemplateLanguage.ENGLISH_US,
12    library_template_button_inputs=[
13        URLButton.library_input(
14            base_url="https://www.example.com/track-order/{{1}}",
15            url_suffix_example="https://www.example.com/track-order/12345",
16        ),
17    ]
18)
19
20# Create the template
21wa.create_template(order_update)
22# CreatedTemplate(id='...', category=TemplateCategory.UTILITY, status=TemplateStatus.PENDING)