Final - Liquid Tag Reference
On this page
The final block tag runs its content after the response has been sent to the client. Use it in a Liquid controller for fire-and-forget work like analytics tracking, logging, webhook notifications, and any cleanup that does not affect the customer’s response.
:::note
This tag works only inside a Liquid controller template (at controllers/<controller>/<action>.liquid). It is a silent no-op outside a controller.
:::
When final runs
In a Liquid controller, execution flows through three phases:
{% before %}block — runs first- Standard action — the main controller logic
- Page rendering — the template output
{% after %}block — runs after rendering{% final %}block — runs after response sent ← you are here
The response has already been sent to the browser, so {% respond %} and {% redirect %} are no-ops in this phase.
Syntax
```liquid
{% final %} {% action “analytics.track”, event: “checkout_complete”, value: current_cart.total %} {% endfinal %} ```
| Property | Value |
|---|---|
| Tag Name | final |
| Type | Block tag |
| Source | StoreConnect |
What’s available in final
- Response already sent — the client has received their response
current_request.params— still accessible- All globals —
current_store,current_customer,current_cart, etc. - Limited tags —
{% action %}works, but NOT{% respond %}or{% redirect %}(client already has response)
Common tasks in final
1. Track analytics after purchase
```liquid
{% before %} {% action “cart.checkout” %} {% endbefore %}
{% after %} {% redirect to: current_store.thank_you_path %} {% endafter %}
{% final %} {% action “analytics.track”, event: “purchase_complete”, order_id: current_cart.order_id, total: current_cart.total %} {% endfinal %} ```
2. Send webhook notification asynchronously
```liquid
{% final %} {%- new Map payload -%} {%- assign payload = payload | set_key: “customer_id”, current_customer.id -%} {%- assign payload = payload | set_key: “event”, “login_successful” -%} {%- assign payload = payload | set_key: “timestamp”, “now” | date: “%Y-%m-%dT%H:%M:%SZ” -%} {% endfinal %} ```
3. Perform cleanup or logging
```liquid
{% before %} {% params session_id: current_request.params.session -%} {% endbefore %}
{% final %} {% action “logging.record”, session_id: current_request.params.session_id, status: “completed”, duration: “5000ms” %} {% endfinal %} ```
Key behaviors
- Runs after response sent — the client has already received their response; you cannot change it
{% respond %}is a no-op — the response has been sent; attempting to respond does nothing{% redirect %}is a no-op — the client already has their response; redirects cannot be applied- Fire-and-forget operations — use this phase for analytics, logging, webhooks, and cleanup
- API is forced async — all actions in final are run asynchronously; do not rely on synchronous completion
Why use final
The final block is useful for:
- Analytics and tracking — record user actions without affecting response time
- Webhooks — notify external systems asynchronously
- Logging and audit trails — record events for compliance or debugging
- Cleanup operations — reset session state, clear temporary data
- Background tasks — send emails, update counts, sync data
Because final runs after the response is sent, long-running operations here do not delay the customer’s experience.
Related phases
{% before %}— runs before rendering; use for validation and actions{% after %}— runs after page rendering; use for responses or redirects- See Liquid controller lifecycle for the complete flow
Was this article helpful?
Thanks for your feedback! It helps us improve our docs.