Skip to main content

TTC (Time‑To‑Completion)

Purpose: Provide a clear, consistent, and concise reference for anyone who needs to measure, instrument, report, and interpret TTC in a product.

1. Overview

What is TTC?
The elapsed time from the moment a user initiates a business‑level action (e.g., “Add to Cart”, “Submit Order”, “Start Upload”) until the system reports that the action is completed successfully.

Why it matters?
Directly correlates with perceived performance and conversion.
Faster TTC → higher satisfaction & revenue.

What can be measured?
Front‑end user‑journey events only. Server‑side performance.mark‑type timestamps are excluded because they do not map to a user‑visible milestone. |

TermDefinition
Start pointThe UI event that indicates the user has begun the flow (e.g., button click, form submit).
End pointThe UI event that signals the operation finished (e.g., success toast, navigation to a confirmation page, API‑success callback).
TTC valueendTimestamp - startTimestamp measured in milliseconds.

2. TTC Measure Instance

2.1 TTC Singleton Object

In TTC Measure instance, we will introduce a singleton object that will be used on each page. This singleton will store any events that are invoked in the event marking function.

FieldTypeNotes
<event_name>ITTCEventEntryEvent name that's already registered in datadog metrics tag.
startTimenumber/undefinedValue of performance.mark() or performance.now(), default unset
endTimenumber/undefinedValue of performance.mark() or performance.now(), default unset
statusstring/undefinedValue to determine if the event is successful, default unset. Possible values: 'success', 'timeout', 'failure'
configITTCEventConfigConfig for related event

Sample filled object:

const ttcEvents = {
// #### Sample completed event entry
"card_form_completion": {
startTime: 2112040.099999994,
endTime: 2129180.099999994
status: 'success',
config: {
sendToDDLogs: true
}
},

// #### Sample ongoing event entry
"payment_page_to_click_paynow": {
startTime: 2112040.099999994,
endTime: undefined,
status: undefined,
config: {}
}

// #### Sample event entry that has not started
"method_selection": {
startTime: undefined,
endTime: undefined,
status: undefined,
config: {
onlyRecordOnce: true
}
}
}

2.2 TTC Config

As each event may behave differently, we'll allow the product team to define each event's behavior.

By default:

  • Events can be recorded multiple times
  • If event has started, calling markTTCEventStart() will be ignored until markTTCEventEnd() is called
  • Marking the start of an event will not override the previous start time
  • Events will not be sent to DD logs
  • Both successful and failed events will be sent to DD logs if sendToDDLogs is true
  • Timeout is not set
Config propertyTypeNotes
onlyRecordOncebooleanWhether to only record the event once, default false. If true, subsequent calls to markTTCEventStart() and markTTCEventEnd() for the same event will be ignored
overrideStartTimebooleanWhether to override the mark start of an event, default false. If true, calling markTTCEventStart() will override the previous start time and clear up existing performance marks of start events
preserveMarksAndMeasuresAfterUploadbooleanWhether to preserve marks and measures after upload, default false. If true, performance marks and measures related to the event will not be cleared after uploading the event data. Useful for event visualizations in devTool
sendToDDLogsbooleanWhether to send the event to DD logs, default false
sendFailedOnlyToDDLogsbooleanWhether to send only failed events to DD logs, default false
timeoutnumberOptional timeout for the event (in ms), default unset. If the event is not ended within the timeout period after it is started, markTTCEventEnd will be automatically called with status = 'timeout'.

2.3 TTC Hooks & Instance Creation

To detect if a page wants to enable TTC measurement, the intended page only needs to import useTTCMeasure() hooks.

  • On the product main component, use useTTCMeasure() hooks with ttcConfig. Can refer to product usage example on Section 4.2.
  • When ttcConfig and productDomain (from getPublicRuntimeConfig) is defined, it will trigger TTC Measure instance initialization with productDomain, ttcConfig, and trackMetrics callback.
  • After the initialization, the TTC Measure instance will be ready to use on the page.

2.4. Core functions

/**
* React hook to initialize TTC measurement.
*
* This hook should be used in the index/main component of the page.
* It also handles cleanup of TTCMeasure instance on unmount or route change.
* As TTC is a client-side only measurement, this hook does nothing on server-side rendering.
*
* @param ttcConfig - TTC configuration for the current application
* @param onEventUpload - optional callback function that will be called when an event is uploaded
*/
export function useTTCMeasure(
ttcConfig: ITTCConfig,
onEventUpload?: (data: ITTCEventCallbackData) => void
): void;

/**
* Mark the start of an event to be tracked
*
* @param eventName - name of the event to be tracked
* @param data - optional data to be associated with the event (for Datadog logging, supports rich object)
* @param tags - optional tags to be associated with the event (for Datadog metrics, must be registered in BEI repo)
*/
export function markTTCEventStart<T extends string>(
eventName: T,
data: Record<string, any> = {},
tags: Record<string, string> = {}
): void;

/**
* Mark the end of an event to be tracked
*
* @param eventName - name of the event to be tracked
* @param status - status of the event, possible values: 'success', 'failure', 'timeout'. Default 'success'
* @param data - optional data to be associated with the event (for Datadog logging, supports rich object)
* @param tags - optional tags to be associated with the event (for Datadog metrics, must be registered in BEI repo)
*/

export function markTTCEventEnd<T extends string>(
eventName: T,
status: ITTCEventEntry['status'] = 'success',
data: Record<string, any> = {},
tags: Record<string, string> = {}
): void;

/**
* Set common tags for TTC measurement, the tag will be applied for all TTC events in the current page.
*
* @param tags - common tags to be set, MUST be registered in BEI repo
*/
export function setTTCCommonTags(tags: Record<string, string>);
FlowmarkTTCEventStart locationmarkTTCEventEnd location
LoginonSubmit of login form (immediately before API call)Success callback of login request (or error handler)
PaymentClick on “Pay” buttonNavigation to guideline page (or other callbacks)
File UploadonChange of file input (after file is selected)onUploadSuccess / onUploadError callback

2.6. Caveats

  • Do not use markTTCEventStart on the server side as a TTC marker – it does not reflect the user journey on devTools.
  • Do not start/stop TTC inside generic utilities that may be reused across unrelated flows; keep the start/end pair tight to the UI action.
  • Do not set unregistered tags during marking events or setting common tags. Tags sent must be available in BEI repo first before use. See Section 3.1.

3. Reporting

3.1 TTC Datadog Metrics

Every time TTC Measure instance is initialized, it will read getPublicRuntimeConfig().productDomain value to determine product owner of importing page. This productDomain value will then be used as datadog metrics name.

Why don't we use centralized metric like TTI?

  • As TTC is highly coupled with product needs, the tags needed may differ per domain.
    • If we use centralized metrics, there may be some tags that actually are not used, but all domains must track anyway, since the tags are shared across all domains.
  • Cost-wise, having multiple domain metrics and centralized metrics will be similar as the pricing is pay-per-use.

Built-in DD Metrics Logging

PropertyValue Example
Metric Nameweb.[domain].performance.page.ttc
country'id', 'au'
interface'desktop', 'mobile'
locale'en-id', 'ja-jp'
is_bot'true', 'false', common tag filtered based on aws waf known_bot header
event_name'form_completion', 'submit_payment', 'item_selection'
status'success', 'timeout', 'failure'

Important Notes

  • Metric name must be registred to BEI repo before use if your domain hasn't had one yet. See DD metrics registration guide.
  • Country, interface, is_bot, and locale will be always be sent
  • event_name and status will be sent for all TTC events
  • Other tags specific for your product usage can be added along with marking events, or set using setTTCCommonTags() function.

3.2 TTC Datadog Logs

Datadog logging is optional for TTC. Product team can setup the config if they would like to include datadog logging for each events.

PropertyEntryValue Example/Format
Name@nameweb.[domain].performance.page.tti
service@servicewebfpr-desktop, webpla-mobile
page@meta.pathname/flight/fullsearch, /car-rental
locale@meta.route_prefixen-id, en-en
app_version@props.app_versionrelease_webgtr_20251112-e17b158559
data.end@props.data.endData sent when marking end
data.start@props.data.startData sent when marking start
duration@props.duration2000, 52223
eventName@props.eventName'form_completion', 'submit_payment', 'item_selection'
endTime@props.endTimePerformance marking time for the end of the event
startTime@props.startTimePerformance marking time for the start of the event
status@props.status'success', 'failure', 'timeout'
message@props.messageTTC measure message

4. Product Usage

4.1 Datadog Metrics Registration

  1. [First time only] Make changes to the BEI repo to register new metrics.
    Register:

    • Metrics owner registration
    • Metrics name and tags -> Format: web.[domain].performance.page.ttc
    • Tag group & possible values -> This determines which tags you can send during TTI marking events.

    See sample PR: https://github.com/traveloka/bei-observability-platform/pull/410

    This step is optional, only for first time integration if you don't have web.[domain].performance.page.ttc metrics in Datadog yet.

  2. Make changes to BEI repo to add eventNames you'd like to track.
    By registering eventName, your metrics can filter and create SLOs by the eventName value.

    This step is mandatory for any new event name registration.

  3. Request review to techops-platform channel and provide tag series combination estimation.
    Sample tag series calculation:

    - Interface: 5 interfaces
    - Event_name: 10 events
    - Currencies: 29 currencies
    In total: 5 x 10 x 29 = 1450 series for payment web TTC

Add useTTCMeasure to the main/top component of the page. This will initialize TTC Measure instance on the client. Along with the useTTCMeasure, we will pass a TTC config object which will determine how we treat each of the events. For an event that uses default config, you can set an empty object as the value.

// packages/payment/app-desktop/page/search/index.tsx
import { useTTCMeasure, ITTCConfig } from '@traveloka/core';

// Define event names and configs related to each event
const ttcMeasureConfig: ITTCConfig = {
search_form_input: { overrideStartTime: true, sendFailedOnlyToDDLogs: true },
page_load_to_first_item_click: { onlyRecordOnce: true, sendToDDLogs: true },
item_selection: {
sendToDDLogs: true,
preserveMarksAndMeasuresAfterUpload: true,
timeout: 30000,
},
card_form_completion: {}, // default config,
};

const Page = () => {
// Construct TTC measure instance for this page
useTTCMeasure(ttcMeasureConfig);

return <></>;
};

4.3 Invoke event start & end markers

After the TTC instance was created, the markTTCEventStart() and markTTCEventEnd() function is ready to use according to the intended product flow.

// container/CreditCardForm.tsx
import { markTTCEventStart, markTTCEventEnd } from '@traveloka/core';

export function CreditCardForm() {
const [isFormLoaded, setFormLoaded] = setState(false)

// Mark TTC event start when credit card form loaded
React.useEffect(() => {
if (isFormLoaded) {
markTTCEventStart('card_form_completion')
}
}, [isFormLoaded])

// Mark TTC event end when form validated
function validateForm() {
const isValid = validate()
if (!isValid) return;
markTTCEventEnd('card_form_completion', true, { trigger: 'GPO' }, { invoice_type: 'standard'})
}

return (...)
}

4.4 [Optional] Set common DD metrics tags for all events

If you need to send a custom tag for all events within your page, instead of passing it to all of the event markers, you can utilize the setTTCCommonTags() function.
Example:

import { setTTCCommonTags } from '@traveloka/core';

export function CreditCardForm() {
const {currency} = useAPIFetcher()

// Mark TTC common tags when related data source has loaded
React.useEffect(() => {
setTTCCommonTags({
currency,
})
}, [currency, pageSource])

return (...)
}

4.5 Sample reporting

When markTTCEventEnd() is invoked, it will directly send the ttc tracking to datadog.
Based on the example above, tracking sent will be:

  • Datadog metrics: web.payment.performance.page.ttc
    {
    "value": 2000,
    "tags": {
    "event_name": "card_form_completion",
    "invoice_type": "standard",
    "currency": "IDR",
    "status": "success"
    }
    }
  • Datadog log: service:webpay-desktop
    {
    "name": "web.payment.performance.page.ttc",
    "props": {
    "app_version": "release_webpay_asdfghjk",
    "duration": 2000,
    "eventName": "card_form_completion",
    "message": "[TTC Measure] card_form_completion success with duration: 2000ms",
    "data": {
    "end": {
    "trigger": "GPO"
    },
    "start": {}
    },
    "startTime": 1000,
    "endTime": 3000,
    "status": "success"
    }
    }

5. Common FAQs

QuestionAnswer
Can I reuse the same event_name for different pages?Yes, we differentiate event_name ownership by metric name web.<domain>.performance.page.ttc. So multiple event_names can be triggered anywhere as long as it's still registered under the same metric name
What if the end point is a third‑party redirect?Emit the markTTCEventEnd before leaving the page (e.g., in the click handler that triggers the redirect).
Should I include network latency?Yes – TTC is end‑to‑end from the user’s perspective, so network time is part of the measurement.
How to handle multiple concurrent TTCs with the same event_name?There can only be 1 event happening for 1 event_name at once. When it has ended, you can start a new event with the same event name.