<div id='api_efauhzwmhkgq' class='api_div' data-search-keywords='track banner analytics events external_user_id app_id app_version events id event_type timestamp events_processed message errors type index'>
<h1 id="track-banner-analytics-events">Track Banner analytics events</h1>
<div class="api_type"><div class="method post ">post</div>
<p>/v1/device-messaging/banners/track</p>
</div>

<p><strong>Important:</strong></p>

<p>This page is in beta. Features and documentation for the Device Messaging API are subject to change.</p>

<blockquote>
  <p>Use this endpoint to record impression and click events for Banners.</p>
</blockquote>

<p>Braze validates each event separately. When a request contains both valid and invalid events, Braze processes the valid events and returns details about skipped events in the <code class="language-plaintext highlighter-rouge">errors</code> array. If no events are valid, Braze returns a <code class="language-plaintext highlighter-rouge">400</code> status code.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>To use this endpoint, you need the following:</p>

<ul>
  <li>A workspace with Banners enabled</li>
  <li>A <a href="/docs/api/device_messaging_api/authentication">client-side REST API key</a> with the <code class="language-plaintext highlighter-rouge">banners.track</code> permission</li>
  <li>The <a href="/docs/api/basics#endpoints">REST endpoint</a> for your Braze instance</li>
  <li>A Banner <code class="language-plaintext highlighter-rouge">id</code> returned by the <a href="/docs/api/device_messaging_api/endpoints/banners/post_sync_banners">Retrieve Banners for a user endpoint</a></li>
</ul>

<p>Include the client-side REST API key in the <code class="language-plaintext highlighter-rouge">Authorization</code> header as a bearer token.</p>

<h2 id="rate-limit">Rate limit</h2>

<p>Rate limits apply per workspace. If you exceed the rate limit, Braze returns a <code class="language-plaintext highlighter-rouge">429</code> status code. When available, use the <code class="language-plaintext highlighter-rouge">X-RateLimit-Limit</code>, <code class="language-plaintext highlighter-rouge">X-RateLimit-Remaining</code>, <code class="language-plaintext highlighter-rouge">X-RateLimit-Reset</code>, and <code class="language-plaintext highlighter-rouge">X-RateLimit-Retry-After</code> response headers to monitor your usage and determine when to retry.</p>

<p>For more information, see <a href="/docs/api/device_messaging_api/rate_limits">Device Messaging API rate limits</a>.</p>

<h2 id="request-body">Request body</h2>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"external_user_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{EXTERNAL_USER_ID}"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"app_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{APP_API_IDENTIFIER}"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"app_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"events"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{BANNER_ID}"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"impression"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-04-09T12:00:00Z"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="request-parameters">Request parameters</h2>

<table class="reset-td-br-1 reset-td-br-2 reset-td-br-3 reset-td-br-4 reset-td-br-5" aria-label="Request parameters">
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Required</th>
      <th>Data type</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">external_user_id</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The external ID of the user associated with all events in the request. The UTF-8 encoded value must be fewer than 987 bytes.</td>
      <td><code class="language-plaintext highlighter-rouge">user_abc123</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">app_id</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The <a href="/docs/api/identifier_types#app-identifier">app API identifier</a>. It must identify an app in the authenticated workspace.</td>
      <td><code class="language-plaintext highlighter-rouge">26a39c72-e647-4766-b62e-4521fa2dae59</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">app_version</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The version of the host app. It must not exceed 255 characters.</td>
      <td><code class="language-plaintext highlighter-rouge">1.0.0</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">events</code></td>
      <td>Required</td>
      <td>Array of objects</td>
      <td>One or more Banner analytics events to record.</td>
      <td><code class="language-plaintext highlighter-rouge">[{"id":"bnr_01HZ3K2QFGH9XVNJ4W8PCRMT5E","event_type":"impression","timestamp":"2026-04-09T12:00:00Z"}]</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">events[].id</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The Banner <code class="language-plaintext highlighter-rouge">id</code> returned by the Retrieve Banners for a user endpoint. Use the Banner ID, not the <code class="language-plaintext highlighter-rouge">placement_id</code>, so Braze attributes the event to the correct campaign and variant.</td>
      <td><code class="language-plaintext highlighter-rouge">bnr_01HZ3K2QFGH9XVNJ4W8PCRMT5E</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">events[].event_type</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The event type. Possible values are <code class="language-plaintext highlighter-rouge">impression</code> and <code class="language-plaintext highlighter-rouge">click</code>.</td>
      <td><code class="language-plaintext highlighter-rouge">impression</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">events[].timestamp</code></td>
      <td>Required</td>
      <td>String</td>
      <td>The date and time when the event occurred, formatted as an <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> string.</td>
      <td><code class="language-plaintext highlighter-rouge">2026-04-09T12:00:00Z</code></td>
    </tr>
  </tbody>
</table>

<h2 id="example-request">Example request</h2>

<p>Replace <em><code class="language-plaintext highlighter-rouge">YOUR_REST_API_URL</code></em> with the <a href="/docs/api/basics#endpoints">REST endpoint</a> for your Braze instance.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre>curl <span class="nt">--location</span> <span class="nt">--request</span> POST <span class="s1">'{YOUR_REST_API_URL}/v1/device-messaging/banners/track'</span> <span class="se">\</span>
<span class="nt">--header</span> <span class="s1">'Authorization: Bearer {YOUR_CLIENT_SIDE_REST_API_KEY}'</span> <span class="se">\</span>
<span class="nt">--header</span> <span class="s1">'Content-Type: application/json'</span> <span class="se">\</span>
<span class="nt">--data-raw</span> <span class="s1">'{
  "external_user_id": "user_abc123",
  "app_id": "26a39c72-e647-4766-b62e-4521fa2dae59",
  "app_version": "1.0.0",
  "events": [
    {
      "id": "bnr_01HZ3K2QFGH9XVNJ4W8PCRMT5E",
      "event_type": "impression",
      "timestamp": "2026-04-09T12:00:00Z"
    },
    {
      "id": "bnr_01HZ3K2QFGH9XVNJ4W8PCRMT5E",
      "event_type": "click",
      "timestamp": "2026-04-09T12:00:05Z"
    }
  ]
}'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="response-parameters">Response parameters</h2>

<table class="reset-td-br-1 reset-td-br-2 reset-td-br-3" aria-label="Response parameters">
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Data type</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">events_processed</code></td>
      <td>Integer</td>
      <td>The number of events that Braze validated and queued.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">message</code></td>
      <td>String</td>
      <td>The status of the accepted event batch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">errors</code></td>
      <td>Array of objects</td>
      <td>Details about events that Braze skipped. This array is absent when Braze processes all events.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">errors[].type</code></td>
      <td>String</td>
      <td>The validation error for the skipped event.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">errors[].index</code></td>
      <td>Integer</td>
      <td>The zero-based index of the skipped event in the request’s <code class="language-plaintext highlighter-rouge">events</code> array.</td>
    </tr>
  </tbody>
</table>

<h2 id="example-responses">Example responses</h2>

<h3 id="all-events-processed">All events processed</h3>

<p>When Braze accepts all events, it returns a <code class="language-plaintext highlighter-rouge">202</code> status code.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"events_processed"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="some-events-skipped">Some events skipped</h3>

<p>Braze also returns a <code class="language-plaintext highlighter-rouge">202</code> status code when it accepts at least one valid event. The response identifies any skipped events.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"events_processed"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Invalid event_type. Valid types are: impression, click."</span><span class="p">,</span><span class="w">
      </span><span class="nl">"index"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="no-valid-events">No valid events</h3>

<p>If Braze can’t process any events, it returns a <code class="language-plaintext highlighter-rouge">400</code> status code.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"No valid events provided."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Invalid event_type. Valid types are: impression, click."</span><span class="p">,</span><span class="w">
      </span><span class="nl">"index"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"'timestamp' is required"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"index"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="status-codes">Status codes</h2>

<table class="reset-td-br-1 reset-td-br-2" aria-label="Status codes">
  <thead>
    <tr>
      <th>Status code</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">202</code></td>
      <td>Braze accepted at least one event. The response lists any skipped events.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">400</code></td>
      <td>The request is malformed, required fields are invalid, or no events are valid.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">401</code></td>
      <td>The client-side REST API key is missing or invalid.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">403</code></td>
      <td>The client-side REST API key doesn’t have the <code class="language-plaintext highlighter-rouge">banners.track</code> permission.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">404</code></td>
      <td>The Banners feature isn’t enabled for the workspace.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">429</code></td>
      <td>The workspace exceeded its rate limit.</td>
    </tr>
  </tbody>
</table>

<p>For more information, see <a href="/docs/api/device_messaging_api/error_handling">Device Messaging API error handling and retries</a>.</p>

</div>
