# Sync Audiences to Facebook Custom Audience

<p>This guide lists the steps to configure Facebook Custom Audience as a destination in RudderStack and sync the created audiences to it.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="setup">Setup</h2><ol>
<li>Set up an audience in RudderStack using the <a href="https://www.rudderstack.com/docs/audiences/audience-builder/create-audience/" >Audience Builder</a>.</li>
<li>Go to the <strong>Syncs</strong> tab of the audience page and click <strong>Add sync</strong>. Then, select <strong>Facebook Custom Audience</strong>.</li>
<li>Click <strong>New destination</strong> — you can also select an already-configured destination here.</li>
<li>Specify a unique name to identify this destination in RudderStack.</li>
<li>In the <strong>Event delivery account</strong> section, click <strong>Select</strong> to choose from an existing Facebook Ads account from the list. You can also click <strong>Add account</strong> and authenticate using a Facebook API access token:</li>
</ol>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th><div>Description</div></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Account Name</td>
          <td>Specify a unique Custom Audience account name</td>
      </tr>
      <tr>
          <td>Access Token</td>
          <td>Enter the <a href="#where-can-i-find-the-user-access-token-for-the-application" >access token</a> of your business application set up for accessing the Facebook Marketing API</td>
      </tr>
      <tr>
          <td>App Secret</td>
          <td>This field is <strong>optional</strong>. Enter the <a href="https://developers.facebook.com/docs/facebook-login/security/#appsecret" >app secret</a> from the <strong>Basic app settings</strong> page of your <a href="https://developers.facebook.com/" >Facebook Developer account</a></td>
      </tr>
  </tbody>
</table>
<ol start="6">
<li>The <strong>Ad Account ID</strong> field is pre-populated based on the selected Facebook Ads account — select the ID.</li>
</ol>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="configure-sync">Configure sync</h2><p>This section lists the settings to correctly configure your audience syncs to the Facebook Custom Audience destination.</p>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text"><p>The following two audience types are supported:</p>
<ul>
<li><a href="https://developers.facebook.com/docs/marketing-api/audiences/guides/custom-audiences" >Custom Audiences</a></li>
<li><a href="https://en-gb.facebook.com/business/help/917879191754763?id=401668390442328" >Value-based lookalike audiences</a> — see <a href="#create-new-audience" >Create new value-based lookalike audiences</a> section for more information.</li>
</ul></div>
  </div>
</blockquote>

</html>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="select-audience">Select audience</h3><p>In this section, you can create a new audience or use an existing one.</p>
<ul>
<li><strong>Create new audience</strong>: Use this option to create a new custom audience in Facebook. You can specify the name and description of the new Facebook audience.</li>
</ul>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text">If you select <strong>Create new audience</strong>, a new audience is created in Facebook with the same <strong>Ad Account ID</strong> configured in the <a href="#setup" >connection settings</a>.</div>
  </div>
</blockquote>

</html>
<ul>
<li><strong>Use existing audience</strong>: Select an existing custom audience. This field is populated based on your <a href="#setup" >connection settings</a>.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="sync-mode">Sync mode</h3><p>RudderStack supports only <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-modes/#mirror-mode" >Mirror mode</a> for this integration.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="map-identifiers">Map identifiers</h3><p>In this section, you can map your warehouse columns to specific Custom Audience fields that represent your audience&rsquo;s identity.</p>

<figure class="image--main "  >
    <a 
         href="/docs/images/audiences/destinations/fb-custom-audience/map-identifiers-syncs.webp"
        
        >
        <img src="/docs/images/audiences/destinations/fb-custom-audience/map-identifiers-syncs.webp" 
         
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="advanced-settings">Advanced settings</h3><table>
  <thead>
      <tr>
          <th>Setting</th>
          <th><div>Description</div></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Automatically hash PII data</td>
          <td>When set to <strong>Yes</strong>, user data is hash-encoded irrespective of whether the data is already hashed or not. <br /><br /><strong>Note</strong>:  Facebook expects the user data to be hash encoded using <code>SHA256</code>. <br /><br />See <a href="#data-hashing" >Data hashing</a> section for more information.</td>
      </tr>
      <tr>
          <td>Normalize data formatting</td>
          <td>When set to <strong>Yes</strong>, Facebook automatically cleans and standardizes the audience data, like emails and phone numbers. <br /><br />See <a href="#data-normalization" >Data normalization</a> section for more information.</td>
      </tr>
      <tr>
          <td>Specify where your data originates from</td>
          <td>Select the field that describes the origin of your data — this helps Facebook classify the user list and apply matching and privacy rules.</td>
      </tr>
      <tr>
          <td>Specify identifier type</td>
          <td>Choose the identifier type that Facebook Ads uses to match users to their profiles.</td>
      </tr>
  </tbody>
</table>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="sync-settings">Sync settings</h2>

<p>Two options are available to schedule your audience syncs:</p>
<ul>
<li><strong>Run syncs automatically</strong> based on a specified frequency (every 5 minutes, 10 minutes, 15 minutes, 30 minutes, 1 hour, 3 hours, 6 hours, 12 hours, or 24 hours) and a specific time (in UTC).</li>
<li><strong>Run syncs manually</strong> — trigger a sync from the sync details page. You will need to run the sync each time.</li>
</ul>

<figure class="image--main "  >
    <a 
         href="/docs/images/audiences/destinations/fb-custom-audience/run-sync.webp"
        
        >
        <img src="/docs/images/audiences/destinations/fb-custom-audience/run-sync.webp" 
         
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="sync-observability-settings">Sync observability settings</h4>

<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Retain sync logs</td>
          <td>This setting is toggled on by default and instructs RudderStack to store the sync logs in your warehouse. You can also configure the below settings: <br /><br /><table><tr><th width="20%">Setting</th><th width="80%">Description</th></tr><tr><td>Sync log retention</td><td>Specify the retention period of the <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-observability/#sync-logs" >sync logs</a> in your warehouse. <br /><br />If you set it to 1, then RudderStack deletes any sync log older than a day (in UTC time). </td></tr><tr><td>Snapshot table retention</td><td>Specify the number of <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-observability/#snapshot-table-schema" >snapshot tables</a> to retain. </td></tr></table></td>
      </tr>
      <tr>
          <td>Retry failed records</td>
          <td>This setting is toggled on by default and causes RudderStack to continually <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-observability/#retry-failed-records" >retry sending the failed records</a>.</td>
      </tr>
      <tr>
          <td>Retain and show failures</td>
          <td>This setting is toggled off by default. Turn it on to have RudderStack write the error recorded for every failed record to a <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/rudderstack-schema/#failed-records-table" >failed records table</a> in your warehouse and show it in the sync&rsquo;s <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/developer-guides/sync-observability/#failed-records" >failed records view</a>. It applies from the next sync and can take up to a minute to reach your data plane. <br /><br /><table><tr><th width="20%">Setting</th><th width="80%">Description</th></tr><tr><td>Failure retention</td><td>Number of days RudderStack keeps each sync&rsquo;s failed records in your warehouse. The default is 30 and the minimum is 7.</td></tr></table></td>
      </tr>
  </tbody>
</table>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">Storing sync logs, snapshot tables, and failed records may incur additional warehouse costs.</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="enable-sync">Enable sync</h2>

<p>Once the setup is complete, enable the sync.</p>

<figure class="image--main "  >
    <a 
         href="/docs/images/audiences/destinations/tiktok/enable-sync.webp"
        
        >
        <img src="/docs/images/audiences/destinations/tiktok/enable-sync.webp" 
         
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="manage-value-based-lookalike-audiences">Manage value-based lookalike audiences</h2><p>This section describes the steps to map your warehouse data to a new or existing value-based lookalike audience.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="new-audience">New audience</h4><p>To create a new <a href="https://en-gb.facebook.com/business/help/917879191754763?id=401668390442328" >value-based lookalike audience</a>, map a warehouse column to the <strong>Lookalike Value</strong> field in the <a href="#map-identifiers" >Identifier mappings</a>:</p>
<ol>
<li>In the <a href="#select-audience" >Select audience</a> section, select <strong>Create new audience</strong> and specify the audience name and description.</li>
<li>In the <strong>Choose identifier mappings</strong> window, map a warehouse column to the <strong>Lookalike Value</strong> field.</li>
</ol>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text"><p>Note that:</p>
<ul>
<li>A value-based lookalike audience is not created if you do not include the <strong>Lookalike Value</strong> in your identifier mappings — a <a href="https://developers.facebook.com/docs/marketing-api/audiences/guides/custom-audiences" >custom audience</a> is created instead.</li>
<li>You can update the mapping for the <strong>Lookalike Value</strong> field after the audience is created.</li>
</ul></div>
  </div>
</blockquote>

</html>

<figure class="image--main "  >
    <a 
         href="/docs/images/audiences/destinations/fb-custom-audience/lookalike-value-mapping-new.webp"
        
        >
        <img src="/docs/images/audiences/destinations/fb-custom-audience/lookalike-value-mapping-new.webp" 
         
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<ol start="4">
<li>Set up the other identifier mappings as per your requirement.</li>
<li>Continue with the rest of the setup.</li>
</ol>
<!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="existing-value-based-lookalike-audience">Existing value-based lookalike audience</h4><ol>
<li>In the <a href="#select-audience" >Select audience</a> section, select <strong>Use existing audience</strong> and select the value-based lookalike audience from the dropdown.</li>
<li>In the <strong>Choose identifier mappings</strong> window, map a warehouse column to the <strong>Lookalike Value</strong> field.</li>
</ol>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text">You can update the mapping for the <strong>Lookalike Value</strong> field after the audience is created.</div>
  </div>
</blockquote>

</html>
<ol start="3">
<li>Set up the other identifier mappings as per your requirement.</li>
<li>Continue with the rest of the setup.</li>
</ol>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">You cannot change the audience type from a value-based lookalike audience to a regular custom audience (and vice versa) <strong>after</strong> the audience is created.</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="schema-fields-mapping">Schema fields mapping</h2><p>The following table details the schema field mappings specified in the dashboard:</p>
<table>
  <thead>
      <tr>
          <th>Dashboard field name</th>
          <th>Marketing API schema field</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>EMAIL</code></td>
          <td><code>EMAIL</code></td>
      </tr>
      <tr>
          <td><code>PHONE</code></td>
          <td><code>PHONE</code></td>
      </tr>
      <tr>
          <td><code>GENDER</code></td>
          <td><code>GEN</code></td>
      </tr>
      <tr>
          <td><code>MADID</code></td>
          <td><code>MADID</code></td>
      </tr>
      <tr>
          <td><code>EXTERN_ID</code></td>
          <td><code>EXTERN_ID</code></td>
      </tr>
      <tr>
          <td><code>DOB YEAR (YYYY)</code></td>
          <td><code>DOBY</code></td>
      </tr>
      <tr>
          <td><code>DOB MONTH (MM)</code></td>
          <td><code>DOBM</code></td>
      </tr>
      <tr>
          <td><code>DOB DATE (DD)</code></td>
          <td><code>DOBD</code></td>
      </tr>
      <tr>
          <td><code>LAST NAME</code></td>
          <td><code>LN</code></td>
      </tr>
      <tr>
          <td><code>FIRST NAME</code></td>
          <td><code>FN</code></td>
      </tr>
      <tr>
          <td><code>FIRST NAME INITIAL</code></td>
          <td><code>FI</code></td>
      </tr>
      <tr>
          <td><code>CITY</code></td>
          <td><code>CT</code></td>
      </tr>
      <tr>
          <td><code>US STATES</code></td>
          <td><code>ST</code></td>
      </tr>
      <tr>
          <td><code>ZIP</code></td>
          <td><code>ZIP</code></td>
      </tr>
      <tr>
          <td><code>COUNTRY</code></td>
          <td><code>COUNTRY</code></td>
      </tr>
  </tbody>
</table>
<p>Note that the <code>MADID</code> and <code>EXTERN_ID</code> fields are not hashed.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">RudderStack modifies the schema names visible in the dashboard for readability. However, during the event call, the field names <strong>must be exactly the same as</strong> the schema names specified by Facebook Marketing API, as mentioned in the table above.</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="data-normalization">Data normalization</h2><p>By default, RudderStack formats the data as prescribed by Facebook before sending it to the destination:</p>
<table>
  <thead>
      <tr>
          <th>Schema field name</th>
          <th>Example input</th>
          <th>Formatted output (before hashing)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>EMAIL</code></td>
          <td><code>ABC@gmail.com</code></td>
          <td><code>abc@gmail.com</code></td>
      </tr>
      <tr>
          <td><code>PHONE</code></td>
          <td><code>0@96346895</code></td>
          <td><code>96346895</code></td>
      </tr>
      <tr>
          <td><code>GEN</code></td>
          <td><code>FEMALE</code></td>
          <td><code>f</code></td>
      </tr>
      <tr>
          <td><code>DOBD</code></td>
          <td><code>2</code></td>
          <td><code>02</code></td>
      </tr>
      <tr>
          <td><code>DOBM</code></td>
          <td><code>1</code></td>
          <td><code>01</code></td>
      </tr>
      <tr>
          <td><code>LN &amp; FN</code></td>
          <td><code>Abc,@</code></td>
          <td><code>abc@</code></td>
      </tr>
      <tr>
          <td><code>FI</code></td>
          <td><code>Mr.</code></td>
          <td><code>mr.</code></td>
      </tr>
      <tr>
          <td><code>CT</code></td>
          <td><code>HN#</code></td>
          <td><code>hn</code></td>
      </tr>
      <tr>
          <td><code>ST</code></td>
          <td><code>? AL ?</code></td>
          <td><code>al</code></td>
      </tr>
      <tr>
          <td><code>ZIP</code></td>
          <td><code>11502 @bc</code></td>
          <td><code>11502@bc</code></td>
      </tr>
      <tr>
          <td><code>COUNTRY</code></td>
          <td><code>IN</code></td>
          <td><code>in</code></td>
      </tr>
  </tbody>
</table>
<p>If you enable the <a href="#advanced-settings" >Normalize data formatting</a> setting, user data is formatted as prescribed by the Facebook Marketing API.</p>
<p>After normalization, each field is validated before sending it to Facebook.</p>
<ul>
<li>Invalid fields are <strong>not</strong> sent to Facebook.</li>
<li>If all fields in a row are invalid, then the entire row is rejected and marked as failed</li>
</ul>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text">Data is first normalized, then validated (skipping invalid rows), then <a href="#data-hashing" >hashed</a> before sending to Facebook.</div>
  </div>
</blockquote>

</html>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="data-hashing">Data hashing</h2><p>The <a href="#advanced-settings" >Automatically hash PII data</a> setting lets you hash your user data before sending it to Facebook. This setting is enabled by default.</p>
<ul>
<li>Configure the setting to <strong>Yes</strong> if your incoming data is not pre-hashed</li>
<li>Configure this setting to <strong>No</strong> if your data is already hashed</li>
</ul>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text"><p>Incorrect configuration of the <strong>Automatically hash PII data</strong> setting will result in failures.</p>
<ul>
<li>If the <strong>Automatically hash PII data</strong> setting is configured to <strong>Yes</strong> and your data is pre-hashed, the event will fail.</li>
<li>If the <strong>Automatically hash PII data</strong> setting is configured to <strong>No</strong> and your data is not pre-hashed, the event will fail.</li>
</ul></div>
  </div>
</blockquote>
<p>Such events are rejected with a clear error message, for example:</p>
<div class="rs-code">
  <div class="rs-code__head"><button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">Hashing is disabled but the value for field EMAIL appears to be unhashed. Either enable hashing or send pre-hashed data.</code></pre></div>
</div>
<p>You will also see errors in the <strong>Events</strong> tab for cases that were previously marked as successful but resulted in no matches, helping you identify and fix data quality or configuration issues.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="faq">FAQ</h2><!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="where-can-i-find-the-ad-account-id">Where can I find the Ad Account ID?</h4><p>Go to your Facebook <a href="https://www.facebook.com/adsmanager/manage/" >Ads Manager account</a> where you can find the Ad Account ID in the account&rsquo;s drop-down menu:</p>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/ads-account-id-fb.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/ads-account-id-fb.webp" 
         alt="Audience source"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<p>You can click on <strong>See More Ad Accounts</strong> if the required Ad account is not visible.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="where-can-i-find-the-user-access-token-for-the-application">Where can I find the user Access Token for the application?</h4>
<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">To generate the user access token for your application, you must first <a href="#how-do-i-add-an-app-as-a-system-user-asset-with-manage-permissions" >add it as a system user asset</a> with manage permissions.</div>
  </div>
</blockquote>
<p>Follow these steps to generate a user access token required to use the Facebook Marketing API:</p>
<ol>
<li>Under the system user, click the <strong>Generate New Token</strong> button and select the app from the dropdown.</li>
</ol>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/fb-custom-audience-generate-new-token.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/fb-custom-audience-generate-new-token.webp" 
         alt="Generate new token"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/fb-custom-audience-token-select-app.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/fb-custom-audience-token-select-app.webp" 
         alt="Select app for the token"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<ol start="2">
<li>Choose the <strong>Token expiration</strong> time.</li>
</ol>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/fb-custom-audience-token-settings.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/fb-custom-audience-token-settings.webp" 
         alt="Token settings"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<ol start="3">
<li>Under <strong>Available permissions</strong>, select <code>ads_read</code> and <code>ads_management</code>.</li>
</ol>
<p>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/fb-custom-audience-available-permissions-1.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/fb-custom-audience-available-permissions-1.webp" 
         alt="Token permissions"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

<figure class="image--main "  >
    <a 
         href="/docs/images/event-stream-destinations/fb-custom-audience-available-permissions-2.webp"
        
        >
        <img src="/docs/images/event-stream-destinations/fb-custom-audience-available-permissions-2.webp" 
         alt="Token permissions"  
         
         
        decoding="async" loading="lazy" class="img-shortcode"/>
    </a>
    
</figure>

</p>
<ol start="4">
<li>Click the <strong>Generate Token</strong> button and copy the token credentials.</li>
</ol>
<!-- end-chunk -->
<!-- begin-chunk -->
<h4 id="why-am-i-seeing-the-error-failed-to-update-the-custom-audience-facebook-responded-with-error-code-2650">Why am I seeing the error “Failed to update the custom audience. Facebook responded with error code: 2650?</h4><p>This error is not explicitly documented by Facebook but is a known API-level limitation. <code>Error 2650</code> typically occurs when attempting to add users to a custom audience that was created using a different data source.</p>
<p>For example:</p>
<ul>
<li>Audience originally created via manual file upload</li>
<li>Audience created through another platform or integration</li>
</ul>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text">Facebook does not allow mixing data source types within the same custom audience.</div>
  </div>
</blockquote>

</html>
<br />
