
Form data is usually [tracked by using the SDK](/developers/web/tracking-form-data), but you can make API calls from your backend, so that you have more control over the process and access to detailed event logs. Unlike the SDK, the logic of calling endpoints can also be modified according to business requirements.


<div class="admonition admonition-warning"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" /></svg></div><div class="admonition-body"><div class="admonition-content">

The API calls must be made by the server!  
Making API calls from the browser exposes your API key.

</div></div></div>


The logic described in this article will let you detect if the customer who fills in a form (for example, a log-in form) is the same as the one currently recognized in the browser.

The data is saved in a `form.submit` event. The event contains all data from the form.


<div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

If you implement tracking form data over the API, it is recommended to block sending form.submit events by the SDK. See [Event authentication settings](/docs/assets/events/event-settings).

</div></div></div>


To learn more about our API, see [these articles](/developers/api).
## Implementation overview

The flowchart presents an overview of the logic which replicates the behavior of tracking form data over the SDK.

In certain conditions, some steps of the logic are redundant. Thanks to this, all scenarios are covered with inserting additional checks.


<div class="admonition admonition-warning"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" /></svg></div><div class="admonition-body"><div class="admonition-content">

Depending on your configuration, the unique identifier is email (default setting) or custom ID (see [Identifiers](/docs/settings/configuration/non-unique-emails).)

</div></div></div>



<div class="admonition admonition-note"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

This logic is a suggestion. You can modify it according to your business needs or create your own. Remember to cover all the scenarios.

</div></div></div>


<figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/developers/web/tracking-form-data/_gfx/backend-form-tagging.svg" class="full" alt="Sending form data with API"><figcaption>Overview of tracking form data by using the API</figcaption></figure>

## Implementation details

This section explains in detail the flow of events and actions, and how to implement them.

### Customer enters site and fills in HTML form

HTML form example:


<pre><code class="language-html">&lt;form action="" method="post"&gt;
    &lt;input type="text" name="email" placeholder="Email" value="john.doe@synerise.com" /&gt;
    &lt;input type="text" name="name" placeholder="Name" value="John" /&gt;
    &lt;input type="text" name="surname" placeholder="Surname" value="Doe" /&gt;
    &lt;input type="submit" value="Save" /&gt;
&lt;/form&gt;</code></pre>


1. Gather data from the HTML form.
2. **Optional:** Validate the data in the form (for example, check if email is properly formatted). 
3. **Optional:** Authenticate the customer.
4. Check if the `_snrs_p` cookie has a value for `identityHash`.  
    **`_snrs_p` cookie example:**

   <div class="highlight-code-block" data-hl-lines="5">
   <pre><code class="language-yaml">_snrs_p:
       host:
       permUuid:e0097757-d1e2-44ac-ba3c-d97979a354c1 # deprecated
       uuid:e0097757-d1e2-44ac-ba3c-d97979a354c1 # current UUID
       identityHash:277163923
       init:1613135695
       last:1625149870.406
       current:1625149875
       uniqueVisits:7
       allVisits:17</code></pre>
   </div>

1. Perform one of the following actions:
    - if `identityHash` does not have a value, proceed to [this section](#if-identityhash-does-not-a-have-a-value).
    - if `identityHash` has a value, proceed to [this section](#if-identityhash-has-a-value).

### If identityHash does not a have a value

The customer is currently anonymous. The data from the anonymous account needs to be associated with the data for the customer who filled in the HTML form.

1. Generate an UUIDv5 for the customer. For details, see [this article](/developers/web/uuids#generating-uuidv5).
2. Send a create/update API call to merge the anonymous customer with the one who was identified in the HTML form.  
    The request body must contain an array with two objects: one with the identifier and the new UUIDv5, the other with the identifier and the UUID from the `_snrs_p` cookie.  
    API reference is available [here](https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients).  
    If you want to send additional data, such as agreements or free-form attributes, they must be included in the object with the new UUIDv5.

   <details class="accordion"><summary>See curl example (email)</summary><div class="accordion-content"><p>The highlighted properties are required, the others are optional. For a comprehensive list of properties you can send, see <a href="https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients">API reference</a>.</p> <div class="highlight-code-block" data-hl-lines="8,9,24,25"> <pre><code class="language-plaintext">curl --location --request POST '{SYNERISE_API_BASE_PATH}/v4/clients/batch' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Api-Version: 4.4' \ --header 'Authorization: Bearer eyJ...yeIc' \ --data-raw '[ { "email": "example@synerise.com", "uuid": "4f82f7a9-0745-55a3-b028-e301b19bf8ec", "agreements": { "email": true, "sms": true }, "tags": [ "exampleTag" ], "attributes": { "customAttribute1": true, "customAttribute2": "string", "customAttribute3": 42 } }, { "email": "example@synerise.com", "uuid": "35a1c1d8-2468-4c47-9be2-f2c2edf9f526" } ]'</code></pre> </div></div></details>

    
   <details class="accordion"><summary>See curl example (customId)</summary><div class="accordion-content"><p>The highlighted properties are required, the others are optional. For a comprehensive list of properties you can send, see <a href="https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients">API reference</a>.</p> <div class="highlight-code-block" data-hl-lines="8,9,24,25"> <pre><code class="language-plaintext">curl --location --request POST '{SYNERISE_API_BASE_PATH}/v4/clients/batch' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Api-Version: 4.4' \ --header 'Authorization: Bearer eyJ...yeIc' \ --data-raw '[ { "customId": "example.customId", "uuid": "d2bdec3a-96d9-5805-9ca2-1557aec691b1", "agreements": { "email": true, "sms": true }, "tags": [ "exampleTag" ], "attributes": { "customAttribute1": true, "customAttribute2": "string", "customAttribute3": 42 } }, { "customId": "example.customId", "uuid": "35a1c1d8-2468-4c47-9be2-f2c2edf9f526" } ]'</code></pre> </div></div></details>

1. Send a `form.submit` event. The data from the form is stored in the `params` object.  
    API reference is available [here](https://developers.synerise.com/DataManagement/DataManagement.html#operation/CustomEvent).
    
   <details class="accordion"><summary>See curl example (email)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "email": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>

    
   <details class="accordion"><summary>See curl example (customId)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "customId": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>


4. Store the new UUIDv5 in the browser in one of the following ways:
    - If after SDK initialization: use the `SR.client.setUuid("new_uuid");` method; replace `new_uuid` with the UUID. The UUID is written to the `_snrs_p` cookie immediately.
    - If the Synerise SDK is not initialized, store the UUID in the `_snrs_reset_uuid` cookie. The UUID is written to the `_snrs_p` at SDK initialization.

**Result:** The customer's data is saved, a recognized profile is created.

### If identityHash has a value

The customer is recognized. You need to check if the customer who filled in the HTML form is the same as the one whose data is currently saved in the `_snrs_p` cookie.

1. Generate the `identityHash` of the customer who filled in the form in one of the following ways:
    - If Synerise SDK is initialized, use the following function:
        ```
        SR.client.hashIdentity("unique_id")
        ```
        where `unique_id` is the email or custom ID

    - If Synerise SDK is not initialized, use the following JS code or your own version of it, which can be written in another language:
    
      <pre><code class="language-js">// arguments
      // data: string (email or customId) to create hash from

      // returned value
      // hash: hashed string

      hashString: function (data) {
          var hash = 0;
          if (data.length === 0) {
              return hash;
          }
          for (var i = 0; i &lt; data.length; i++) {
              var char = data.charCodeAt(i);
              hash = ((hash &lt;&lt; 5) - hash) + char;
              hash = hash &amp; hash;
          }
          return hash;
      }</code></pre>

1. Compare the generated hash with `identityHash` from the `_snrs_p` cookie:
    - [`identityHash` is identical](#if-identityhash-is-identical)
    - [`identityHash` is not identical](#if-identityhash-is-not-identical)

#### If identityHash is identical

The customer who filled the form is the same as the one who visited the site previously. You can send their data.

1. Send a `form.submit` event. The data from the form is stored in the `params` object.  
    API reference is available [here](https://developers.synerise.com/DataManagement/DataManagement.html#operation/CustomEvent).
    
   <details class="accordion"><summary>See curl example (email)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "email": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>

    
<details class="accordion"><summary>See curl example (customId)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "customId": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>


**Result:** The data from the form is saved to the customer's account.
#### If identityHash is not identical

The customer who filled the form is not the same who visited the site previously.

1. Generate an UUIDv5 for the customer. For details, see [this article](/developers/web/uuids#generating-uuidv5).
2. Send a create/update API call to create/update the profile which is the new context with the UUIDv5.  
    The request body is a single-object array. The object contains the new UUIDv5 and the unique identifier.
    If the profile already has this UUIDv5, sending the same data again does not cause any problems. You do not need to check before sending the call.  
    API reference is available [here](https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients).
    
   <details class="accordion"><summary>See curl example (email)</summary><div class="accordion-content"><p>The highlighted properties are required, the others are optional. For a comprehensive list of properties you can send, see <a href="https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients">API reference</a>.</p> <div class="highlight-code-block" data-hl-lines="8,9"> <pre><code class="language-plaintext">curl --location --request POST '{SYNERISE_API_BASE_PATH}/v4/clients/batch' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Api-Version: 4.4' \ --header 'Authorization: Bearer eyJ...yeIc' \ --data-raw '[ { "email": "example@synerise.com", "uuid": "4f82f7a9-0745-55a3-b028-e301b19bf8ec", "agreements": { "email": true, "sms": true }, "tags": [ "exampleTag" ], "attributes": { "customAttribute1": true, "customAttribute2": "string", "customAttribute3": 42 } } ]'</code></pre> </div></div></details>

    
   <details class="accordion"><summary>See curl example (customId)</summary><div class="accordion-content"><p>The highlighted properties are required, the others are optional. For a comprehensive list of properties you can send, see <a href="https://developers.synerise.com/ClientManagement/ClientManagement.html#operation/BatchAddOrUpdateClients">API reference</a>.</p> <div class="highlight-code-block" data-hl-lines="8,9"> <pre><code class="language-plaintext">curl --location --request POST '{SYNERISE_API_BASE_PATH}/v4/clients/batch' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Api-Version: 4.4' \ --header 'Authorization: Bearer eyJ...yeIc' \ --data-raw '[ { "customId": "example.customId", "uuid": "d2bdec3a-96d9-5805-9ca2-1557aec691b1", "agreements": { "email": true, "sms": true }, "tags": [ "exampleTag" ], "attributes": { "customAttribute1": true, "customAttribute2": "string", "customAttribute3": 42 } } ]'</code></pre> </div></div></details>

    **Result:** The profile is created/updated with the new UUIDv5 and the additional data you sent. The UUIDv5 is always the same for a given customer, regardless of where and how it was generated.
1. Send a `form.submit` event. The data from the form is stored in the `params` object.  
    API reference is available [here](https://developers.synerise.com/DataManagement/DataManagement.html#operation/CustomEvent).
    
   <details class="accordion"><summary>See curl example (email)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "email": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>

    
   <details class="accordion"><summary>See curl example (customId)</summary><div class="accordion-content"><pre><code class="language-plaintext">curl --location --request POST 'https://{SYNERISE_API_BASE_PATH}/v4/events/custom' \ --header 'Authorization: Bearer eyJh...T-hyeIc' \ --header 'Api-Version: 4.4' \ --header 'Content-Type: application/json' \ --data-raw '{ "label": "Customer submitted a form", "action": "form.submit", "client": { "customId": "example@synerise.com" }, "params": { "firstname": "John", "lastname": "Doe", "formType": "exampleFormType" } }'</code></pre></div></details>


4. Store the new UUIDv5 in the browser in one of the following ways:
    - If after SDK initialization: use the `SR.client.setUuid("new_uuid");` method; replace `new_uuid` with the UUID. The UUID is written to the `_snrs_p` cookie immediately.
    - If the Synerise SDK is not initialized, store the UUID in the `_snrs_reset_uuid` cookie. The UUID is written to the `_snrs_p` at SDK initialization.

    **Result:** The customer context in the browser changes to the customer with the new UUIDv5.

## Marketing agreements

See [Newsletter agreements](/developers/web/newsletter-agreements).