Redirect URL Templating
Bookla can dynamically personalize the success, failure, and cancel redirect URLs that your payment provider sends a customer back to after checkout. Instead of a single static URL, you can define a template URL with placeholders (e.g. {{.ID}}, {{.Amount}}), and Bookla will fill them in with the details of the actual transaction before handing the URL to the payment provider.
To make sure nobody can tamper with those values on the way back to your site, every templated URL is also cryptographically signed with a secret only you know. You can then ask Bookla to verify the signature before trusting anything you read from the query string.
This guide explains:
- What you can put into a redirect URL template
- How to enable the feature for a provider
- How the signed URL that reaches your customer looks
- How to verify that signature on your side
When to use it
Use redirect URL templating when your post-payment page needs to know something specific about the transaction, for example:
- Showing a confirmation like "Booking #123 paid" without an extra API call
- Passing the booking or reference ID to your own analytics / CRM
- Redirecting to a different page depending on the item type or plugin
- Reconciling the redirect to an order in your own system
Because the templated parameters are signed, you can safely use them to drive UI or lookups, as long as you validate the signature first.
Supported providers
Redirect URL templating is available for the following providers:
- Stripe
- Klix
- Decta
- Montonio
- MercadoPago
Each provider exposes two settings you can toggle:
enable_url_templating— enables placeholder substitution and signingsigning_secret— the shared secret used to compute and verify the HMAC signature
Both are set per company, alongside the existing success_redirect_url, failure_redirect_url and cancel_redirect_url fields for that provider.
If enable_url_templating is false (the default), Bookla passes the configured redirect URLs to the provider as‑is — no substitution, no signing. Existing integrations keep working unchanged.
Enabling templating
-
Open your payment provider settings in the Bookla dashboard (e.g. Stripe, Klix, …).
-
In each redirect URL field you want to template, replace static values with placeholders (see the reference below). For example:
https://yourshop.com/booking/success?booking={{.ItemID}}&ref={{.ReferenceID}}&amount={{.Amount}}¤cy={{.Currency}} -
Set Enable URL templating to
true. -
Set a Signing secret — a long, random string that only your backend will know. Treat it like an API key: never ship it to the browser, and rotate it if you suspect it has leaked.
-
Save the settings.
From this point on, every time Bookla hands a redirect URL to the provider, it will first substitute the placeholders and then append two extra query parameters — ts and sig — that you can use to verify the URL.
If enable_url_templating is true but signing_secret is missing or empty, Bookla will refuse to create a payment URL for that transaction. Always configure both fields together.
Placeholder reference
Placeholders use Go text/template syntax: a dot followed by the field name, wrapped in double curly braces. The leading dot is required — {{ID}} without the dot is invalid.
| Placeholder | Description | Example value |
|---|---|---|
{{.ID}} | Unique ID of the payment transaction in Bookla | d2c1b0c4-7a3e-4d7e-9a7b-3c2d1e0f0a1b |
{{.ItemID}} | ID of the item being paid for (e.g. booking ID) | e2f3a4b5-6789-4abc-def0-0123456789ab |
{{.ItemIDs}} | Comma‑separated list of item IDs if the transaction covers multiple items | id1,id2,id3 |
{{.ItemType}} | Type of item the transaction pays for | booking, ticket, subscription |
{{.PluginNamespace}} | Namespace of the plugin that initiated the payment, if any | com.example.plugin |
{{.Amount}} | Transaction amount in the smallest currency unit (cents, etc.) | 5000 (= 50.00) |
{{.Currency}} | ISO 4217 currency code | EUR, USD, GBP |
{{.ReferenceID}} | Your own reference for the transaction, if one was provided when booking | ORDER-456 |
Notes:
Amountis always an integer in the smallest currency unit of the transaction currency (cents for EUR/USD, etc.). It is not divided by 100 for you.ItemIDsis a single string, not repeated query parameters. If you need each ID as its own parameter, split the string on,on your side after verification.- Only these fields are available. Any other placeholder (e.g.
{{.CustomerEmail}}) will cause template processing to fail.
A complete example
Template configured in the dashboard:
https://shop.example.com/payments/done?txID={{.ID}}&item={{.ItemID}}&type={{.ItemType}}&amt={{.Amount}}&curr={{.Currency}}&ref={{.ReferenceID}}
URL that Bookla will ultimately hand to the payment provider:
https://shop.example.com/payments/done?
txID=d2c1b0c4-7a3e-4d7e-9a7b-3c2d1e0f0a1b
&item=e2f3a4b5-6789-4abc-def0-0123456789ab
&type=booking
&amt=5000
&curr=EUR
&ref=ORDER-456
&ts=1713600000
&sig=9f3c…64‑hex‑chars…a1
(Line breaks added for readability — the real URL is a single line.)
Template syntax rules
A few things to keep in mind when authoring templates:
- Use dotted field names.
{{.ID}}is valid,{{ID}}is not. Bookla will return an error that explicitly reminds you of this if you get it wrong. - Values are automatically URL‑encoded. Bookla applies path‑style escaping (
%20for spaces, etc.) to every placeholder value before substitution, so you don't need to — and you must not — wrap fields in{{urlquery ...}}. Doing so would double‑encode the value. - Static query parameters are preserved. Anything you put in the template that is not a placeholder (e.g.
?source=checkout) is passed through untouched and is not included in the signature. - Mix placeholders freely in path and query. Both work:
/payments/{{.ID}}/doneand?id={{.ID}}are valid. - URL length is capped at 2000 characters after substitution and signing. If your template plus data exceeds this, Bookla will fail payment URL generation rather than produce an unusable link.
The signed URL
Once the placeholders are filled in, Bookla appends two query parameters to the URL before it leaves the platform:
| Parameter | Meaning |
|---|---|
ts | Unix timestamp (seconds) at which the URL was signed |
sig | Hex‑encoded HMAC‑SHA256 of the templated parameters and the timestamp |
Only the parameters that Bookla actually templated contribute to the signature. Static query parameters and non-templated path segments are ignored. This means you can add extra tracking parameters to a redirect on your side without breaking verification — as long as you don't touch the templated ones, ts or sig.
A signature is valid for 5 minutes from its ts. A small amount of clock skew (up to 1 minute) into the future is tolerated, but older signatures or timestamps more than a minute ahead of the server clock will be rejected.
Verifying the signature on your side
When your success / failure / cancel page loads, you should:
- Collect all query parameters you received.
- Call Bookla's verify‑signature endpoint, forwarding those parameters.
- Only trust the values in the URL if Bookla returns
valid: true.
Endpoint
GET /companies/{company_id}/payments/verify-signature
company_id— the UUID of the company whose redirect URL was signed.- All the query parameters you received on the redirect (including
tsandsig) must be forwarded as‑is.
Response
{
"valid": true
}
or, when verification fails:
{
"valid": false,
"reason": "signature_expired"
}
Reason codes
The reason field uses a stable set of values you can branch on:
| Reason | What it means |
|---|---|
missing_parameters | ts or sig is missing from the URL. |
invalid_timestamp | ts is not a valid integer. |
signature_expired | ts is older than the 5‑minute validity window. |
timestamp_in_future | ts is more than 1 minute ahead of Bookla's server clock. |
invalid_signature_format | sig is not valid hexadecimal. |
invalid_signature_length | sig is not 64 hex characters (32 bytes of HMAC‑SHA256). |
signature_mismatch | The signature does not match — the URL was tampered with, or the secret is wrong. |
company_settings_not_found | No payment settings exist for the given company. |
provider_settings_not_found | The company has no configured preferred provider settings. |
signing_secret_not_configured | Templating was not set up for this provider, so there is nothing to verify. |
failed_to_extract_signing_secret | Bookla couldn't read the signing secret from the provider settings. |
internal_error | An unexpected error happened while verifying. Safe to retry. |
Example flow
User is redirected to:
https://shop.example.com/payments/done?txID=d2c1…&amt=5000&curr=EUR&ts=1713600000&sig=9f3c…a1
Your backend receives the request and calls:
GET https://us.bookla.com/api/v1/companies/{company_id}/payments/verify-signature
?txID=d2c1…&amt=5000&curr=EUR&ts=1713600000&sig=9f3c…a1
Bookla responds:
{ "valid": true }
Your backend now trusts txID, amt, currency and renders the confirmation.
You do not need to implement HMAC yourself — always prefer the verify‑signature endpoint. It is the single source of truth for which parameters are signed, how the signing string is built, and when a signature has expired.
Security notes
- Keep the signing secret secret. It only lives in Bookla's settings and on your own backend. Never expose it to the browser or to the payment provider.
- Always verify before trusting. The templated parameters are convenience — a confirmation page that reads
amountstraight from the URL without calling the verify endpoint can be spoofed. - Rotate if leaked. Rotating the
signing_secretimmediately invalidates any unverified links that are still floating around in customers' tabs or email inboxes. Users mid‑checkout will need to start a new payment. - Don't rely on it for authorization. A valid signature proves the URL was issued by Bookla for a specific transaction — it doesn't replace your own session / auth when performing actions like refunding or modifying a booking.
Troubleshooting
failed to process URL templateerror when creating a payment. Usually means a placeholder has the wrong shape. Double‑check you used{{.FieldName}}with a leading dot and an exact field from the reference.- Signature always returns
signature_expired. Your server clock and Bookla's may be drifting. The allowed window is 5 minutes old and up to 1 minute in the future; make sure your host runs NTP. - Signature always returns
signature_mismatch. Confirm thesigning_secretconfigured in Bookla exactly matches what you expect, and that you're forwarding all query parameters (including any you weren't expecting) to the verify endpoint — not just the ones you recognize. - URL generation fails with a length error. Your template plus transaction values exceeds 2000 characters. Drop unnecessary placeholders or shorten static parts of the URL.
- Redirect works but
ts/sigare missing.enable_url_templatingis probably off, or the template URL contains no placeholders at all — Bookla only signs URLs that were actually templated.