FAQs

Q: What is partner.js?

partner.js is Agoda's frontend SDK solution that enables partners to securely associate each booking attempt with a verified browser session. It generates a booking token that must be included in booking requests sent to Agoda's Affiliate Book API. This token allows Agoda to perform enhanced fraud analysis during booking processing, helping protect both partners and customers

Q: Why do I need to integrate partner.js?

For all Agoda merchant-of-record transactions, partners must integrate with partner.js. This is a mandatory requirement that:

  • Enables enhanced fraud detection and prevention
  • Provides verified session and device context for each booking
  • Helps reduce fraudulent transactions and chargebacks
  • Ensures compliance with Agoda's booking security requirements

Q: What are the main steps of the integration?

The Partner-SDK integration has three core steps:

  1. Load Partner-SDK on your website.
  2. Call init() to initialize a browser session and load the required fraud-detection components.
  3. Call checkout() before booking submission to generate sdk_context_token.

Conditional step: A conditional 3DS flow is required if the Book API returns requiredAction: "payment_challenge" and "status": 202.

Q: Where should I include the partner.js script?

Required:

  • Checkout page (mandatory)

Recommended:

  • Home/Landing page
  • Search results page
  • Property details page

Loading partner.js earlier in the customer journey (e.g., landing page) establishes the session sooner, providing better coverage and more comprehensive fraud detection signals.

Q: Can the script be loaded asynchronously?

Yes. Asynchronous loading is supported. Call init() only after the Partner-SDK script has loaded.

Q: How do I initialize the SDK?

After the script loads, call and await window.partnerSDK.init(config). Provide the required siteId and environment values. To enable optional card tokenization, also provide partnerName. You may provide cspNonce when needed.

async function initializePartnerSdk() {
  try {
    await window.partnerSDK.init({
      siteId: "12345",
      partnerName: "partner-test", // Optional; enables PAN tokenization
      environment: "sandbox",
      cspNonce: "rAnd0m12345",
    });

    enableBookingUI();
  } catch (error) {
    showPartnerSdkUnavailableError(error);
  }
}

Q: Is Partner-SDK blocking? Will it slow down my page?

Partner-SDK collects eligible browser, session, and device signals asynchronously and is designed not to block normal page rendering. Load the script asynchronously and call init() after it loads. Partners should still validate the integration against their own performance requirements.

Q: Does Partner-SDK use cookies?

Yes. Partner-SDK creates a first-party payment_session_id cookie to maintain the browser session. It expires four hours after creation. The SDK also reads Forter's forterToken cookie, when available, to collect fraud-detection signals.

Q: When and how do I generate sdk_context_token?

Call checkout() immediately before submitting the booking from the frontend. The Promise resolves with { success: true, sdk_context_token } and rejects on any failure with { success: false, error: { code, message } }. Handle rejection with try/catch; send a successful token unmodified to your backend and do not submit the booking after a failure.

try {
  const result = await window.partnerSDK.checkout();

  await submitBooking({
    sdk_context_token: result.sdk_context_token,
  });
} catch (failure) {
  handleCheckoutError(failure.error);
}

Q: What parameters can I pass to checkout()?

checkout() accepts two top-level parameters:

window.partnerSDK.checkout(
  params?: Partial<PaymentCardDetails>,
  cb?: CheckoutCallback,
): Promise<CheckoutSuccessResult>;
ParameterTypeRequiredDescription
paramsPartialNoOptional card details used for card tokenization.
cbCheckoutCallbackNoOptional notification callback. Providing it does not consume or replace the returned Promise: the Promise still resolves on success and rejects on failure.

The optional params object supports:

FieldTypeRequired for tokenizationDescription
creditCardNumberstringYesCard number to tokenize.
cardHolderNamestringYesCardholder name.
expiryDatestringYesCard expiry date in MMYYYY format.
cvcstringYesThree- or four-digit card verification code.

When partnerName is configured, all four card fields are required. Missing or incomplete fields cause checkout() to reject with TOKENIZE_FAILED, and no sdk_context_token is returned. When partnerName is not configured, the params object is optional; tokenization is skipped and checkout() can still return an sdk_context_token without tokenized card information.

Promise usage without card tokenization:

const result = await window.partnerSDK.checkout();

Promise usage with card tokenization:

const result = await window.partnerSDK.checkout({
  creditCardNumber,
  cardHolderName,
  expiryDate,
  cvc,
});

Callback usage:

window.partnerSDK.checkout(params, (result, error) => {
  if (result) {
    submitToken(result.sdk_context_token);
  } else if (error) {
    handleCheckoutError(error);
  }
}).catch(() => {
  // The callback already handled the checkout failure.
});

Q: Is PAN tokenization mandatory?

No. PAN tokenization is optional.

To use it, provide partnerName to init() and provide creditCardNumber, cardHolderName, expiryDate, and cvc through the params argument of checkout(). Partner-SDK tokenizes the card details before returning sdk_context_token.

After tokenization, send the tokenized payment data expected by the Affiliate Book API. Raw PAN, CVC, expiry date, and other raw card details must not be sent in the Affiliate Book API request or retained in partner backend systems, logs, or analytics.

Using tokenization may reduce the PCI DSS scope of partner backend systems because those systems no longer need to receive or process raw PAN details. It does not automatically remove the partner or its frontend from PCI DSS scope. The applicable scope depends on how the payment page captures and transmits card data and must be confirmed with the partner's acquirer or PCI assessor. See PCI SSC tokenization guidance.

Q: What happens if partnerName is incorrect?

Use the exact partnerName supplied by Agoda. A value with invalid syntax, such as uppercase letters or an underscore, causes init() to reject. A syntactically valid but unregistered name can pass init(); if the subsequent tokenization request fails, checkout() rejects with TOKENIZE_FAILED and returns no sdk_context_token. Correct partnerName before retrying checkout().

Q: What happens if token generation or card validation fails?

checkout() rejects with { success: false, error: { code, message } } and returns no sdk_context_token. INVALID_CONFIG means the required SDK configuration is missing or invalid. TOKENIZE_FAILED means the SDK was expected to tokenize the card but could not complete tokenization. This includes missing or invalid card details, card-validation failures, and tokenization request failures. Handle the rejection with try/catch and do not submit the booking. Correct the input or configuration, or resolve the request failure, before retrying.

try {
  const result = await window.partnerSDK.checkout(params);
  const token = result.sdk_context_token;
} catch (failure) {
  handleCheckoutError(failure.error);
}

Q: What happens when the Affiliate Book API requires 3DS?

The 3DS flow begins when the Affiliate Book API returns:

{
  "status": 202,
  "requiredAction": "payment_challenge",
  "statusToken": "<opaque-status-token>"
}

The partner backend must:

  • Pass that exact statusToken to the frontend.
  • Retain the token and corresponding booking context for backend status polling.
  • Poll the Affiliate Booking Status API until the authoritative final booking outcome is available.

The frontend must pass the opaque statusToken to window.partnerSDK.handleChallenge(). The Affiliate Booking Status API does not initiate handleChallenge().

Q: How do I call handleChallenge()?

Pass only the statusToken returned by the Affiliate Book API. handleChallenge() resolves with { success: true } when the challenge completes successfully; all unsuccessful outcomes reject with { success: false, reason, error? }. Handle the rejected Promise without waiting for it before processing backend booking-status results, and use failure.reason for control flow:

// The partner backend polls booking status independently.
void window.partnerSDK.handleChallenge(statusToken).catch((failure) => {
  handleChallengeFailure(failure.reason, failure.error);
  // Optional: when failure.reason is 'cancelled', your integration may
  // stop backend status polling for this attempt. See the Step 8 note.
});

An optional container and callbacks may also be supplied. Callbacks are notifications and do not consume or replace the returned Promise, which still rejects on failure:

// The partner backend polls booking status independently.
void window.partnerSDK.handleChallenge(statusToken, {
  container: "#payment-challenge",
  onFailure: ({ reason, error }) => {
    handleChallengeFailure(reason, error);
    // Optional: when reason is 'cancelled', your integration may stop
    // backend status polling for this attempt. See the Step 8 note.
  },
}).catch(() => {
  // onFailure already handled the rejected challenge result.
});

A successful handleChallenge() result confirms only that the browser challenge completed successfully. It does not confirm the booking. The Affiliate Booking Status API result is authoritative.

Q: What happens while the browser challenge is running?

The partner backend should poll the Affiliate Booking Status API while the browser runs handleChallenge(). Continue polling while the response indicates processing: true, including after waitTime has elapsed. Stop when the Affiliate Booking Status API returns a terminal booking outcome. Move the customer to the confirmation or thank-you page only after the Affiliate Booking Status API returns a confirmed booking result.

Note: If handleChallenge() reports reason: 'cancelled', you may stop polling the Booking Status API for that attempt. This does not confirm that the booking was cancelled or failed.

Q: How should statusToken be handled?

Treat statusToken as opaque.

Do:

  • Pass the Affiliate Book API response value unchanged to the frontend.
  • Pass it unchanged to handleChallenge().
  • Retain it securely on the backend for the corresponding booking-status flow.

Do not:

  • Decode, parse, or modify it.
  • Persist it to localStorage, sessionStorage, or any other storage.
  • Reuse it for another API or booking.
  • Log it.
  • Attempt to interpret its contents.

Q: How should sdk_context_token be handled?

Treat sdk_context_token as opaque.

Do:

  • Keep it in memory only as long as necessary.
  • Send it unchanged to the partner backend.
  • Include its unchanged value in the corresponding Affiliate Book API request.

Do not:

  • Decode, parse, or modify it.
  • Persist it to localStorage, sessionStorage, or any other storage.
  • Reuse it for another API or booking.
  • Log it.
  • Attempt to interpret its contents.
  • Pass it to handleChallenge().

Q: What is Content Security Policy, and why must I update it?

Content Security Policy, or CSP, controls which resources a browser may load and which network destinations it may contact.

Partner-SDK may load additional resources and send browser beacons for fraud detection, analytics, and telemetry. Your CSP must allow the required hosts for each applicable directive.

Without these permissions, the browser may block Partner-SDK resources or supporting network calls.

Q: Which CSP directives and hosts are required?

Merge the following Partner-SDK hosts into your site's existing CSP:

DirectiveRequired Partner-SDK hosts
script-srchttps://cdn6.agoda.net, https://beacon.riskified.com, https://bento.agoda.com, https://*.forter.com, https://*.cloudfront.net
connect-srchttps://*.agoda.com, https://*.travelwebservice.com, https://bento.agoda.com, https://*.riskified.com, https://*.forter.com, https://*.cloudfront.net, wss://cdn0.forter.com
worker-srchttps://www.agoda.com, https://*.agoda.com
img-srchttps://img.riskified.com

These entries cover Partner-SDK resources only. Preserve any additional CSP sources required by your application.

Q: How should I implement the CSP update?

Setting CSP through an HTTP response header is recommended because it provides centralized and consistent policy management. Merge these values into the existing policy rather than replacing the application's full CSP.

Content-Security-Policy:
  script-src 'self' https://cdn6.agoda.net https://beacon.riskified.com https://bento.agoda.com https://*.forter.com https://*.cloudfront.net;
  connect-src 'self' https://*.agoda.com https://*.travelwebservice.com https://bento.agoda.com https://*.riskified.com https://*.forter.com https://*.cloudfront.net wss://cdn0.forter.com;
  worker-src 'self' https://www.agoda.com https://*.agoda.com;
  img-src 'self' https://img.riskified.com;

Q: What if my site uses CSP nonces?

Pass the page's nonce to Partner-SDK through cspNonce:

await window.partnerSDK.init({
  siteId: "12345",
  environment: "production",
  cspNonce: "rAnd0m12345",
});

Include the corresponding nonce source in the site's script-src directive: 'nonce-YOUR_NONCE_VALUE'. The value passed as cspNonce must match the nonce used by the page's CSP.

Q: What does my backend need to do?

The partner backend must:

  • Accept sdk_context_token from the frontend.
  • Include its unchanged value in the corresponding Affiliate Book API request.
  • Inspect the Affiliate Book API response for status: 202 and requiredAction: "payment_challenge".
  • Pass the returned statusToken to the frontend when a challenge is required.
  • Poll the Affiliate Booking Status API until the authoritative final booking outcome is available.

Use the Affiliate Book API documentation for the exact Book request field name and location. The sdk_context_token value itself must not be decoded, parsed, or modified.

Q: How do I pass sdk_context_token from the frontend to the backend?

Send sdk_context_token as part of the booking submission to your backend. The transport between the partner frontend and backend is partner-owned. It may be sent in the booking request body or through another established application mechanism. Regardless of the transport method, the value must remain unchanged.

Note: You should be aware of practical cross-browser limits on query string lengths. Passing in the body of a request is a safer option.

Q: What data does Partner-SDK collect?

Partner-SDK collects eligible browser, session, and device context signals used for fraud detection and screening. Card details are processed only when the partner enables optional card tokenization and supplies all four card fields to checkout().

Q: Partner-SDK loads, but beacons do not run under a strict CSP. What should I check?

Check the browser console and network panel for CSP violations. Confirm that the applicable script-src, connect-src, worker-src, and img-src directives include all Partner-SDK hosts listed above. If the site uses nonces, confirm that the page nonce is also passed through cspNonce.

Q: Partner-SDK works on checkout, but the fraud signals are weak or inconsistent. What should I do?

Load and initialize Partner-SDK earlier in the booking journey. For stronger coverage, initialize it on every page in the booking journey through checkout, beginning with the home or landing page when possible.

Q: Is production testing available for the Partner-SDK booking flow?

Production test cards are not available.

Use the sandbox environment to verify that:

  • Partner-SDK is integrated correctly.
  • Requests match the expected schema.
  • Success and failure results are handled correctly.
  • The conditional 3DS flow is implemented correctly.

Where production fraud-screening validation is required, partners may create fully refundable live bookings according to the agreed production-testing process.

Q: Which fraud-related cases should partners test?

The existing testing guidance identifies the following cases:

  • Bookings where the originating IP address differs from the customer IP address.
  • Bookings where one generic IP address is used for all customers.
  • Bookings where one generic customer email address is reused.
  • Bookings where one generic customer phone number is reused.

Production testing must follow the partner's approved testing and refund procedures.


Did this page help you?