Parameter Builder Library: Server-Side Onboarding Guide

Meta has provided a list of library SDKs in both client-side (JavaScript) and server-side (PHP, Java, Python, NodeJS, Ruby). These SDK libraries are intended to help developers improve the quality of Conversions API event parameters (for example, fbc and fbp), and enable advertisers to adhere to Meta’s best practices around generating these parameters.

Server-Side Library Overview

Below are step-by-step guides for integrating with your application directly. If you are interested in running a demo, please refer to the quick start guides.

Currently, the parameter builder feature manages the storing and retrieval of:

  • Meta click ID (fbc)
  • Meta browser ID (fbp)
  • Client IP Address (client_ip_address)

It also provides the normalization and hashing functionality of the following customer information parameters:

  • Email (em)
  • Phone Number (ph)
  • First Name (fn)
  • Last Name (ln)
  • Date of Birth (db)
  • Gender (ge)
  • City (ct)
  • State (st)
  • Zip Code (zp)
  • Country (country)
  • External ID (external_id)

This document describes the parameter builder library from the server side.

For information about the client-side library, please refer to the Client-Side Parameter Builder Library Onboarding Guide. Our recommendation is to implement both the client- and server- side solutions.

Quick-Start Guides

Check our Github for the example demo and code resources: github.com/facebook/capi-param-builder

Add the Meta Library as a Dependency

// Check the latest version. Add the dependency in your composer.json.    
{
   "require": {
 	  "php": ">=7.4",
        "facebook/capi-param-builder-php": "{current_version}"
   },
   "minimum-stability": "dev"
}
// Update the dependencies in your package.json. Run npm install. 
"dependencies": {
      "capi-param-builder-nodejs": {version}
}
// Example below is in Gradle Groovy. Edit your build.gradle:
dependencies {
    implementation 'com.facebook.capi.sdk:capi-param-builder:{current_version}'
}
// Install the library using pip. Run the following command lines.
pip install capi_param_builder_python
        
// Verify the installation by ```pip list```
gem install capi_param_builder_ruby

Using the APIs

Note: We are updating the API to be more flexible to fit your case. Please check each language’s README for the most specific option. Below is the general guidance for all languages.

The parameter builder library provides functionality to process incoming HTTP requests and extract param values such as fbc/fbp and client IP address. It also provides customer information parameters (PII) normalization and hashing functionality. If you’d like to review existing examples, see the quick start guides for more information.

Here are the steps:

  1. Import the library
  2. Call the API
  3. Set the _fbc and _fbp cookies returned by the library
  4. Call the get APIs to get fbc, fbp, client_ip_address and customer information parameters (PII) such as email, phone number, etc.
  5. Send these customer information parameters to Meta using the Conversions API

Example of server-side integration pseudocode:

// Pseudocode on client's server: 

// Call library. Param is list of etld+1 for your websites. 
$param_builder = new ParamBuilder(arrays['example.com', 'test.com']);

// Get fbc, fbp
$cookies = $param_builder->processRequest($host, $url, $cookies, $referer, $x_forwarded_for, $remote_address);

// Save fbc and fbp provided by $cookies
set_cookie($cookies);

// Get fbc
$fbc = $param_builder->getFbc();
// Get fbp
$fbp = $param_build->getFbp();

// Get client ip address
$client_ip_address = $param_builder->getClientIpAddress();

// Get normalized and hashed email 
$email = $param_builder->getNormalizedAndHashedPII(‘John_Smith@gmail.com’,’email’);

// Get normalized and hashed phone 
$phone = $param_builder->getNormalizedAndHashedPII(‘+1 (616) 954-78 88’,’phone’);

// Call Conversion API and pass $fbc, $fbp, $client_ip_address, $email and $phone   
     

We currently support five major APIs:

  • processRequest
  • getFbc
  • getFbp
  • getClientIpAddress
  • getNormalizedAndHashedPII

Different languages may have different name conventions. Below are examples for Java.

processRequest API

This is the core API to process fbc, fbp and client_ip_address. The first 3 parameters for processRequest API are mandatory and the rest are optional, but we encourage you to set all parameters for optimized performance.

/**
 * @param host: host or domain this request is coming from
 * @param query_params: url query params in Map format. Eg. Example.com?test=123&name=hello, then the pass-in query params will be {“test”:”123”, “name”:”hello”}
 * @param cookies: current cookies in Map format. Eg.{“my_cookie1”:”test”, “_fbc”:”xxxxx”} etc. 
 * @param referer: http referrer request header. Eg.https://example.com/page?q=123 etc.
 * @param x_forwarded_for: http x_forwarded_for request header. Eg.203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348 etc.
 * @param remote_address: the remote_address variable in http request. Eg.2001:db8:85a3:8d3:1319:8a2e:370:7348 returned from request.getRemoteAddr() etc.
 * @return List of CookieSetting. You could set your user cookies based on the returned value. 
*/
List<CookieSetting> processRequest(String host, Map<String, String> query_params, Map<String, String> cookies, String referer, String x_forwarded_for, String remote_address)     
     

CookieSetting is a model Meta provides.

@Getter
public class CookieSetting {
 public String name;
 public String value;
 public String domain;
 public int maxAge;
}     
     

Application usage:

import com.facebook.capi.paramsdk.ParamBuilder;
import com.facebook.capi.paramsdk.model.CookieSetting;

...

// Calling the API
// The list would be a list of preferred domains. We'll match it with your input domain and return in the CookieSetting's domain
ParamBuilder paramBuilder = new ParamBuilder(Arrays.asList("example.com", ..));
..
// Currently only contains _fbc and _fbp
List<CookieSetting> updatedCookieList = paramBuilder.processRequest(domainName, queryParamsMap, cookieMap, referer, x_forwarded_for, remote_address);

// Set cookie based on your language and frameworks
...     
     

getNormalizedAndHashedPII API

This API returns the normalized and hashed (sha256) customer information parameters (PII). If input is invalid, this API will return null. This method takes two parameters. The first parameter is the PII value to normalize and hash. The second parameter is the type of PII you want to normalize as. Available options are: 'phone', 'email', 'first_name', 'last_name', 'date_of_birth', 'gender', 'city', 'state', 'zip_code', 'country' and 'external_id'.

// @params piiValue: string. The PII value we want to normalize and hash.
// @params dataType: string. The type of PII you want to normalize as. Available options are: 'phone', 'email','first_name','last_name','date_of_birth','gender','city','state', 'zip_code', 'country' and 'external_id'
// @return NormalizedAndHashedPII: string or null, the normalized and hashed PII. We will return null with invalid input.
getNormalizedAndHashedPII(piiValue,dataType) 

Usage example:
getNormalizedAndHashedPII(‘John_Smith@gmail.com’,’email’);
getNormalizedAndHashedPII(‘+1 (616) 954-78 88’,’phone’);
   
     

getFbc API

You will need to call processRequest above first in order to get the accurate fbc. If fbc is not available, it will return null.

String getFbc();     
     

Application usage:

String fbc = paramBuilder.getFbc();     
     

getFbp API

You will need to call processRequest above first in order to get the accurate fbp. If fbp is not available, it will return null.

String getFbp();     
     

Application usage:

String fbp = paramBuilder.getFbp();     
     

getClientIpAddress API

Return client_ip_address value from cookie. You will need to call processRequest above first in order to get the accurate client_ip_address. If client_ip_address is not available, it will return null.

String getClientIpAddress();     
     

Application usage:

String client_ip_address = paramBuilder.getClientIpAddress();     
     

Manage Cookie Interactions

This section includes some examples to read and store cookies. The server side library doesn’t save cookies; it’s recommended that advertisers save the suggested cookies list. As for user’s cookie and consent, please refer to Meta’s Business Tools Terms for details.

This section includes examples to read and store cookies.

Integration with cookie consent

Businesses can implement code that creates a banner and requires affirmative consent (for example, an “I agree” checkbox at the top of the page) to allow cookie saving actions for fbc and fbp. If you already have a system in place that addresses this need, such as a tag manager, you can make this code optional.

Below are examples of how to read and set the cookies. If you are unsure about any portion, see our quick start guides for more detailed examples.

$param_builder = new FacebookAds\ParamBuilder(arrays['example.com', 'test.com']);
$cookies_to_set = $param_builder->processRequest(
   $_SERVER['HTTP_HOST'],
   $_GET,
   $_COOKIE,
   $_SERVER['HTTP_REFERER'] ?? null, // Optional field. If you input, could help improve the quality
   $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
   $_SERVER['REMOTE_ADDR'] ?? null
);


foreach ($cookies_to_set as $cookie) {
   setcookie(
       $cookie->name,
       $cookie->value,
       time() + $cookie->max_age,
       '/',
       $cookie->domain
   );
   
}
// This PHP example uses drupal/eu_cookie_compliance as third-party cookie consent and may be replaced with whatever the customer uses. For more details, please review the full example at https://www.google.com/url?q=https://capi-automation.s3.us-east-2.amazonaws.com/public/php/example/drupal.zip&sa=D&source=docs&ust=1747687304480502&usg=AOvVaw0z75y_J_zIyHprgIfHq0lt    
/**
   * Fetches cookies from the HTTP request.
   */
  public function onKernelRequest(RequestEvent $event) {
    $request = $event->getRequest();
    $http_host = $request->getHost();
    $get_params = $request->query->all();
    $referer = $request->headers->get('referer');
    $x_forwarded_for = $request->headers->get('X-Forwarded-For');
    $remote_addr = $request->getClientIp();

    // Get COOKIE values equivalent.
    $cookies = $request->cookies->all();
    // if EU Cookie Compliance module is being used
    if (isset($cookies['cookie-agreed'])) {
      // Get the value of the 'cookie-agreed' cookie.
      $this->cookieAgreed =
        $cookies['cookie-agreed'] == self::COOKIE_AGREED_VALUE;
    }
    if($this->cookieAgreed){
        $this->param_builder->processRequest(
          $http_host,
          $get_params,
          $cookies,
	    $referer ?? null,
    $x_forwarded_for ?? null,
    $remote_addr ?? null
      );
    }
  }
  /**
   * Sets a cookie in the HTTP response.
   */
  public function onKernelResponse(ResponseEvent $event) {
    if($this->cookieAgreed){
      // Add the cookie to the response.
      $response = $event->getResponse();
      foreach ($this->param_builder->getCookiesToSet() as $cookie) {
        $response->headers->setCookie(new Cookie(
          $cookie->name,
          $cookie->value,
          time() + $cookie->max_age,
          '/',
          $cookie->domain
        ));       
      }
    }
  }
const builder = new ParamBuilder([‘example.com’, ‘....’]);


 if (!cookieString) {
   return null;
 }
 const cookies = {};
 const items = cookieString.split('; ');
 for (const item of items) {
     const [name, value] = item.split('=');
     cookies[name] = value;
 }


 const cookiesToSet = builder.processRequest(
   req.headers.host, // host
   params, // query params
   cookies, // current cookie
   req.headers.referer ?? null, // optional, help enhance the accurancy
   req.headers['x-forwarded-for'] ?? null,
   req.socket.remoteAddress ?? null
 );


 const cookies = [];
 for (const cookie of cookiesToSet) {
   cookies.push(cookie.name + '=' + cookie.value + '; Max-Age=' + cookie.maxAge + '; Domain=' + cookie.domain + '; Path=/');
 }
 res.setHeader('Set-Cookie', cookies);

   
}
ParamBuilder paramBuilder = new ParamBuilder(Arrays.asList("example.com", '....'));
   Map<String, String> cookieMap = getCookiesToMap(request.getCookies());
   List<CookieSetting> updatedCookieList =
       paramBuilder.processRequest(
           request.getHeader("host"), request.getParameterMap(), cookieMap);
   for (CookieSetting updatedCookie : updatedCookieList) {
     Cookie cookie = new Cookie(updatedCookie.getName(), updatedCookie.getValue());
     cookie.setMaxAge(updatedCookie.getMaxAge());
     cookie.setDomain(updatedCookie.getDomain());
     response.addCookie(cookie);
   }
from capi_param_builder import ParamBuilder


# Main function declaration
# We support DefaultEtldPlusOneResolver() from demo, please
fbcBuilder = ParamBuilder(["example.com", "...."])
# host: str, queries: dict[str, str], cookies: dict[str, str]
updated_cookies = fbcBuilder.process_request(
     domain_only, query_params, cookie_dict
)


# Set cookie
for cookie in updated_cookies:
           self.send_header(
               "Set-Cookie",
  f"{cookie.name}={cookie.value};Max-Age={cookie.max_age};path=/;domain={cookie.domain}",
           )
for cookie in cookies_to_be_updated do
        response.set_cookie(
            cookie.name,
            value: cookie.value,
            domain: cookie.domain,
            path: "/",
            # for sinatra framework the expires is an absolute ts
            # Check your web framework to have the correct expires.
            expires: Time.now + cookie.max_age)
    end

Passsing fbc/fbp, client_ip_address and PII to the Conversions API

After the cookie is updated, you will need to send fbc/fbp, client_ip_address and PII (email, phone, etc.) back to the Conversions API using the common API. The following are examples of how to get fbc/fbp, client_ip_address and PII (email, phone, etc.) in each language.

// Initialize the builder. 
$param_builder = new FacebookAds\ParamBuilder(array('example.com', 'test.com'));

// Process the request
$cookies_to_set = $param_builder->processRequest(
   $_SERVER['HTTP_HOST'],
   $_GET,
   $_COOKIE,
   $_SERVER['HTTP_REFERER'] ?? null,
   $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null,
   $_SERVER['REMOTE_ADDR'] ?? null
);

// Get fbc
$fbc = builder->getFbc();

// Get fbp
$fbp = builder->getFbc();

// Get client_ip_address
$client_ip_address = builder->getClientIpAddress();

// Get normalized and hashed email (sha256)
$email = builder->getNormalizedAndHashedPII(' John_Smith@gmail.com    ','email');

// Get normalized and hashed phone (sha256)
$phone = builder->getNormalizedAndHashedPII('1(650)123-4567','phone');

// Pass the fbc, fbp, client_ip_address, email and phone number to the Conversions API
// Get builder 
const builder = new ParamBuilder(["example.com", "your-domain.com"]);

// Get cookies from request
const requestCookies = parseCookie(req.headers.cookie);
builder.processRequest(
    req.headers.host, // host
    params, // query params
    requestCookies, // current cookie
    req.headers.referer ?? null, // optional, help enhance the accurancy
    req.headers['x-forwarded-for'] ?? null,
    req.socket.remoteAddress ?? null
  );

const fbc = builder.getCookiesToSet().getFbc();
const fbq = builder.getCookiesToSet().getFbq();
const clientIpAddress = builder.getClientIpAddress();
const email = builder.getNormalizedAndHashedPII(' John_Smith@gmail.com    ','email');
const phone = builder.getNormalizedAndHashedPII('1(650)123-4567','phone');

// Pass the fbc, fbp, clientIpAddress, email and phone number to the Conversions API
ParamBuilder paramBuilder = new ParamBuilder(Arrays.asList("example.com", "your-domain.com"));
   Map<String, String> cookieMap = getCookiesToMap(request.getCookies());
   List<CookieSetting> updatedCookieList =
        paramBuilder.processRequest(
            request.getHeader("host"), request.getParameterMap(), cookieMap);

String fbc = paramBuilder.getFbc(cookieMap);
String fbp = paramBuilder.getFbp(cookieMap);
        
// Pass the fbc and fbp to the Conversions API
fbcBuilder = ParamBuilder(["example.com", "your-domain.com"])
       # host: str, queries: dict[str, str], cookies: dict[str, str]
       updated_cookies = fbcBuilder.process_request(
           domain_only, query_params, cookie_dict
       )
       # after process_request got called, you could call get_fbc() and get_fbp() to get the actual value for fbc and fbp
       fbc = fbcBuilder.get_fbc()


       fbp = fbcBuilder.get_fbp()
        
# Pass the fbc and fbp to the Conversions API
builder = ParamBuilder.new(["localhost", "example.com"]) 
cookies_to_be_updated = builder.process_request(
        host, # current host name
        query_params, # query params as hash type
        cookie_dict, # current cookies as hash type
        referral_link) # optional current referer
# recommended to save cookies_to_be_updated to your cookies.
 
# after process_request got called, you could use get_fbc and get_fbp to get the actual value
fbc = builder.get_fbc()
fbp = builder.get_fbp()
        
# Pass the fbc and fbp to the Conversions API

[Optional] Implement ETLD+1 Resolver

You may consider this option as you are constructing the parameter builder library. We recommend using the domain list option mentioned in the Using the APIs section above.

To achieve the best result, fbc and fbp cookies should be written to the top level domain as much as possible. Internally, the parameter builder libraries use a public suffix list to determine which domain to set cookies to. If you know the eTLD+1 of your web domain, you could implement a more optimized resolver by implementing the ETLDPlus1Resolver interface.

Examples from each language are shown below. You may also review the demo examples in the quick start guides for existing solutions.

class SimpleETLDPlus1Resolver implements ETLDPlus1Resolver {
   public function resolveETLDPlus1($domain) {
       if (isSubdomain($domain, "example.com")) {
           return "example.com";
       }
       throw new InvalidArgumentException("only example.com is supported");
   }
}
/*
//Currently we provide 3 options; you may also implement your own resolver.

1. [Recommended] Resolve etld+1 by default
     const etldPlus1Resolver = new DefaultETLDPlus1Resolver();
2. Resolve by half manual input
     const etldPlus1Resolver = new DefaultETLDPlus1Resolver('www.example.com'); // => 'example.com';
3. Manual identify etld+1 (mostly for localhost test).
     const etldPlus1Resolver = new DummyLocalHostTestResolver('localhost');
4. Implement a new resolver by yourself. Example below:

*/
    
class SimpleETLDPlus1Resolver {
   resolveETLDPlus1(domain) {
	if (isSubdomain(domain, "example.com")) {
	return "example.com";
}
      // throw exception or fallback to other functions
   }
// We provide DefaultETLDPlusOneResolver, which uses Guava InternetDomainName to resolve ETLD+1. If you prefer to do it yourself, follow the example below:

public class SimpleETLDPlusOneresolver implements ETLDPlusOneResolver {
	@Override
	public String resolve(String domain) {
               if (isSubdomain(domain, "example.com")) {
	       return "example.com"
               }
               // throw exception or fallback to other function
        }
}
# We provide a default_etld_plus_one_resolver.py within the example file. Feel free to implement your own solutions. 
        
from capi_param_builder import EtldPlusOneResolver

        
class DefaultEtldPlusOneResolver(EtldPlusOneResolver):
   """
   Default implementation of EtldPlusOneResolver
   """


   def resolve(self, host_name: str) -> Optional[str]:
        # Start your implementation to get etld+1 from host_name
        etld_plus_one = host_name
        # Return the resolved etld+1
        return etld_plus_one
# Please check the Ruby example from the README demo for the full context.
        
require 'capi_param_builder/etld_plus_one_resolver'

class DefaultEtldPlusOneResolver < EtldPlusOneResolver
   def resolve(host_name)
        # Your implementation
        return host_name
   
   end

end