# Basic integration

> Learn how to integrate with Talon.One with a basic example.

> For the complete documentation index, see [llms.txt](https://docs.talon.one/llms.txt).

In this tutorial, let's share information with Talon.One using the main entities: [customer sessions][3] and [customer profiles][2].

:::tip
This tutorial is easier to follow if you have read the following pages:

- [API effects](/docs/dev/integration-api/api-effects)
- [Entities](/docs/dev/concepts/entities/entities-overview)
:::

## Overview

Before getting started with the implementation, let's set up some business context.

Let's imagine that we are setting up an integration between Talon.One and our clothing
store. We already have an Application in the Campaign Manager, and it contains a running
campaign with the following rule:

- Any valid coupon code gives a `20%` discount on the session/cart.

## Choose the SDK and the API

:::important
Communication with our API endpoints should happen **only from a backend environment** to
avoid exposing sensitive authentication credentials to end users.
:::

According to the [integration checklist](/docs/dev/get-started/integration-checklist),
let's select an SDK and the API.

- **SDK:** In our case, let's imagine our project uses Python, so let's use
  the [Python SDK](/docs/dev/sdks/overview).
- **API:** We're working with customer sessions and profiles, so we must use
  the [Integration API](/docs/dev/integration-api/overview).

## Create a Talon.One API key

Create your API key:

1. In your Application, click <Settings className="icon" /> **Settings** > **Integration API Keys**.
1. Click <Add className="icon" /> **Create API Key**.
1. In the **Create API Key** drawer, if you are asked for a key type, select **Production**.
1. In **Key name**, type a name to identify the key.
1. In **Key expiration date**, select a date.

   :::tip
   Avoid choosing expiration dates that fall at the end of the year or during other
   high-traffic periods.
   :::

1. In **Third-party integration**, select **No**.
1. Click **Create API Key**.
1. Click <Copy className="icon" /> to copy the key for use.

:::note
You cannot view or copy the API key after closing the drawer.
:::

This key is only for our Talon.One Application. You can now send requests
to the Integration API from our integration layer:

<Tabs groupId="language">

<TabItem value="python" label="python" default>

```python {8,11} title="Using the SDK to authenticate"

from talon_one.rest import ApiException

# Create configuration with your host destination and authorization using api_key_v1
configuration = talon_one.Configuration(
    host = "https://mycompany.europe-west1.talon.one", # edit the host
    api_key_prefix = {
        "Authorization": "ApiKey-v1"
    },
    api_key = {
        "Authorization": "f725d5a03e091ce9ab8bf51ee6c9ca37066e42f386b08bcf77ceddfe5364560"
    }
)

# Instantiate the IntegrationAPI without credentials
integration_api = talon_one.IntegrationApi(talon_one.ApiClient(configuration))
```

</TabItem>
<TabItem value="curl" label="curl">

To authenticate using curl, we use the HEADER field for each request.

```shell
curl --request PUT 'https://mycompany.europe-west1.talon.one/...' \
--header 'Authorization: ApiKey-v1 f725d5a03e091ce9ab8bf51ee6c9ca37066e42f386b08bcf77ceddfe5364560' \
...
```

</TabItem>
</Tabs>

Now that we are authenticated, we can proceed with the rest of the implementation
that looks as follows:

## Create a customer session

:::note
This section takes care of steps 1 and 2 of the integration landscape:

:::

In Talon.One, a customer cart is represented by a [customer session][3].
This resource is essential, it's used to keep Talon.One updated about what the customer
is doing, and to respond accordingly.

:::note
A Talon.One customer session has nothing to do with a server session. The API
client decides when to close the session.
:::

To create a session and update it, we use the Integration API's
[Update customer session][updateCustomerSession]
endpoint.

A session has a state, in our case we'll create it as `open`,
which is the default.
To understand session states, see [Managing session states][sessionStates].

Back to our context, let's imagine that a shopper started creating a shopping cart. At
this given time, the cart contains a couple of items and a coupon code. Let's
communicate this to Talon.One to check if our coupon rule should be applied.

### Send session information

This session should contain:

- Two cart items which exist in our shop's catalog.
- A coupon (`xmas20`).

To do so, we apply the following steps:

1. Create a `NewCustomerSessionV2` object to be fed to the `updateCustomerSession`
   endpoint. In this case, we do not provide a customer profile ID, which creates an
   anonymous session. For more information about customer profiles, see the [section below](#manage-customer-profiles).
1. Add the two cart items and the coupon to the `NewCustomerSessionV2` object.
1. Send the request.
1. Store the response for later processing.

The following code applies the above steps:

<Tabs defaultValue="python"
  groupId="language"
  values={[
    {label: 'Python', value: 'python'},
    {label: 'curl', value: 'curl'},
  ]}>

  <TabItem value="python">

```python title="Using the SDK to update a session"
# ...
# Instantiate the IntegrationAPI without credentials
integration_api = talon_one.IntegrationApi(talon_one.ApiClient(configuration))

# Prepare a NewCustomerSessionV2 object with the cart items and the coupon code
customer_session = talon_one.NewCustomerSessionV2()
customer_session.cart_items = [
    talon_one.CartItem("Red Spring Blouse", "rdbs-1111", 1, 49, "Shirts"), # name, sku, quantity, price, category
    talon_one.CartItem("Denim Trousers", "dtr-2222", 1, 74, "Trousers"),
]
customer_session.coupon_codes = ["xmas20"]

# Instantiate a new IntegrationRequest object
integration_request = talon_one.IntegrationRequest(customer_session)
```

  </TabItem>
  <TabItem value="curl">

```shell
CURL -X PUT https://mycompany.europe-west1.talon.one/v2/customer_sessions/my_session_id -d
'{
  "customerSession": {
    "state": "open",
    "cartItems": [
      {
        "name": "Red Spring Blouse",
        "sku": "rdbs-1111",
        "quantity": 1,
        "price": 49,
        "category": "Shirts",
      },
      {
        "name": "Denim Trousers",
        "sku": "dtr-2222",
        "quantity": 1,
        "price": 74,
        "category": "Trousers",
      }
    ],
    "couponCodes":["xmas20"]
  }
}'
```

 </TabItem>
</Tabs>

Running this sample executes the request and we now have the response in the
`integration_request` variable.

See also:

- [The Update customer session endpoint][updateCustomerSession]
- [NewCustomerSessionV2 in the Python SDK](https://github.com/talon-one/talon-one-python-sdk/blob/master/docs/NewCustomerSessionV2.md)

### Performance tips

**Do more with one call:** The
[Update customer session](/integration-api#tag/Customer-sessions/operation/updateCustomerSessionV2)
and [Update customer profile](/integration-api#tag/Customer-profiles/operation/updateCustomerProfileV2)
endpoints offer a `responseContent` property that you can use to save API calls.
For example, you can use this property to retrieve the customer
profile information without having to use another endpoint.

**Reduce response time:** When you query the `Update customer session` or
`Update customer profile` endpoint but
do not want the request to modify your Application, set the `runRuleEngine` and `dry`
parameters to `true` to send a [dry request](/docs/dev/integration-api/best-practices#dry-requests) and
get faster response times.
You will still see the returned effects but there will be no change to your Application.

**Use a single event for a single customer action:**
When implementing customer actions, consider that each [event](/docs/dev/concepts/entities/events)
represents a single occurrence of a customer action related to a customer session
or customer profile. To avoid receiving too many 409 responses, ensure that
the customer action does not trigger multiple events in parallel.

### Manage parallel requests

For data integrity purposes, Talon.One allows three parallel Integration API requests _per_
customer **profile** or **session** integration ID.
Parallel requests are queued and processed sequentially.
The queue is **limited to two requests**. Extra requests will receive a 409
_Too many requests are updating this profile/session at the same time_ response.

**We recommend only sending sequential requests.**

This applies to the following endpoints:
- [Update customer profile][cp]
- [Update customer session][cs]
- [Track event](/integration-api#tag/Events/operation/trackEventV2)

For example, consider the following parallel requests:

- One `Update customer profile` request on a given `customerId`. This request is
  being processed.
- Two `Update customer session` requests involving the same `customerId` as the
  request above. These two requests are queued.
- Extra requests, for example, a request to `Track event`, related to the same `customerId`
  will receive a 409 response.

[cp]: /integration-api#tag/Customer-profiles/operation/updateCustomerProfileV2
[cs]: /integration-api#tag/Customer-sessions/operation/updateCustomerSessionV2

## Process effects

:::note
This section takes care of step 5 of the integration diagram:

:::

In the previous section, we sent an update to Talon.One. For every session or profile
update that we send, Talon.One runs all applicable rules and returns the
effects in the API response.

The way we process these effects depends on the SDK. We can register effect
handlers or loop through the effects array in the response. With the Python SDK, we loop
over the effects.

In our case, we have only one rule that applies a 20% discount if the coupon code is
valid so we can expect the following effects:

- [`setDiscount`](/docs/dev/integration-api/api-effects#setdiscount)
- [`rollbackDiscount`](/docs/dev/integration-api/api-effects#rollbackdiscount)
- [`acceptCoupon`](/docs/dev/integration-api/api-effects#acceptcoupon)
- [`rejectCoupon`](/docs/dev/integration-api/api-effects#rejectcoupon)
- [`rollbackCoupon`](/docs/dev/integration-api/api-effects#rollbackcoupon)

All effects are available in [API effects](/docs/dev/integration-api/api-effects).

To process the effects, let's loop through the `effects` array from the response. To
keep the code sample small, let's only look for the `setDiscount` and `rejectCoupon`
effects.

```python title="Using the Python SDK to parse the effects"
# ... auth

# API request
integration_request = talon_one.IntegrationRequest(customer_session)

try:
    api_response = integration_api.update_customer_session_v2(
        "my_session_id", integration_request
    )

    # Parse the effects
    for effect in api_response.effects:
        if effect.effect_type == "setDiscount":
            # Initiating right props instance according to the effect type
            setDiscountProps = integration_api.api_client.deserialize_model(
                effect.props, talon_one.SetDiscountEffectProps
            )

            # Access the specific effect properties
            name = (setDiscountProps.name,)
            value = setDiscountProps.value
            print(
                "setDiscount triggered:\n"
                f" Effect name: {name}\n"
                f" Discount value: {value}"
            )
            # Apply the discount to the cart...

        elif effect.effect_type == "rejectCoupon":
            rejectCouponEffectProps = integration_api.api_client.deserialize_model(
                effect.props, talon_one.RejectCouponEffectProps
            )
            print(f"Coupon was rejected: {rejectCouponEffectProps.rejection_reason}")
            # Display banner to the user...

except ApiException as e:
    print("Exception when calling IntegrationApi->update_customer_session_v2: %s\n" % e)
```

The next step is about processing the effect in our store, so this depends on your
setup and industry. Typically, you would recalculate the cart value to display it to your customer.

We have covered the main steps to manage the whole interaction with Talon.One. The
following section are use cases that are useful to further customize the data
you share with Talon.One.

## Add extra information to a customer session

You might have to pass data that is not supported by the SDK. For example, you can
readily create cart items, but what if we want to pass shipping-related information
and run different promotion rules based on the address?
We can do this with [custom attributes][1].

In our case, let's imagine that we want to pass the **city** of the
delivery address. Let's define a custom attribute in the Campaign Manager:

1. Click <Account className="icon"/> **Account** > <Tools className="icon"/> **Tools** > **Attributes** > **Create Attribute**.
1. In **Entity**, select **Customer session**. This means our
   customer session will store the attributes.
1. In **Type**, select **String**.
1. In **API name**, type `shippingCity`. This is the name to reference in the API call.
1. Fill in all the other fields as needed to enable the attribute in your Application.

We can now set this custom attribute in API calls:

<Tabs defaultValue="python"
  groupId="language"
  values={[
    {label: 'Python', value: 'python'},
    {label: 'curl', value: 'curl'},
  ]}>

  <TabItem value="python">

```python title="Using the Python SDK to pass a custom attribute"
integration_request = talon_one.IntegrationRequest(
    {
        "attributes": {
            "shippingCity": "Seattle",
        }
    },
)
api_response = integration_api.update_customer_session_v2("my_session_id", integration_request)
```

  </TabItem>
  <TabItem value="curl">

 ```shell
CURL -X PUT https://mycompany.europe-west1.talon.one/v2/customer_sessions/my_session_id -d
'{
  "customerSession": {
     "attributes": {
       "shippingCity": "Seattle",
     }
  }
}'
```

  </TabItem>
</Tabs>

## Manage customer profiles

Aside from customer sessions, we can manage [customer profiles][2].
They store the attributes of your customers. For example, a profile stores the identifier,
address, device, location or shopping history data of a customer.

A customer profile is represented inside Talon.One by the `CustomerProfile` entity.

A customer profile is the _owner_ of a customer session and may have many sessions over
its lifetime. Profiles are created and updated using the
[updateCustomerProfile](/integration-api#tag/Customer-profiles/operation/updateCustomerProfilesV2) endpoint,
which works like the `updateCustomerSession` endpoint explained above.

:::note
The identifier of a customer profile is stored in the `integration id`
property.
:::

### Create a customer profile

Let's create a customer profile with id `customer-1234` and store a name and some
shipping information in it:

- We can use any of the built-in attributes to define our customer. You can see the
  available built-in attributes on the [Attributes page](/docs/product/account/dev-tools/manage-attributes#display-attributes)
  in the Campaign Manager.
- We can create custom attributes on the customer entity to store extra information.

Let's use the `Name`, `ShippingCity` and `ShippingRegion` built-in attributes
and a custom attribute named `MyAttribute` in this example. We assume
that `MyAttribute` has been created and assigned to the customer entity.
See [how to create attributes](/docs/product/account/dev-tools/manage-attributes#create-a-custom-attribute).

<Tabs defaultValue="python"
  groupId="language"
  values={[
    {label: 'Python', value: 'python'},
    {label: 'curl', value: 'curl'},
  ]}>

  <TabItem value="python">

```python title="Using the Python SDK to create a profile"
# Define a profile id.
customer_profile_id = 'customer-1234'
# Create the payload of the request.
# We assume that these attributes have been created and
# associated to the Customer Profile entity in the Campaign Manager.
body = talon_one.NewCustomerProfile({
  'Name': "Anthony Ray",
  'ShippingCity': "Seattle",
  'ShippingRegion': "Washington",
  'MyAttribute': "my value"
})
# Execute the request
api_response = integration_api.update_customer_profile_v2(customer_profile_id, body)
```

 </TabItem>
  <TabItem value="curl">

```shell
CURL -X PUT https://mycompany.europe-west1.talon.one/v2/customer_profiles/customer-1234 -d
'{
  "attributes": {
    "Name": "Anthony Ray",
    "ShippingCity": "Seattle",
    "ShippingRegion": "Washington",
    "MyAttribute": "my value"
  }
}'
```

  </TabItem>
</Tabs>

We can now use this customer profile with our sessions.

### Connect a customer profile to a customer session

In the examples used for the customer session tutorial above, we used anonymous sessions.
These sessions are not connected to any customer profile, which is not recommended.

To connect a session to a profile, let's set the `profile_id` property of the session to
the value of the customer profile id:

<Tabs groupId="language">

<TabItem value="python" label="python">

```python title="Using the Python SDK to bind a customer to a session"
# Create the customer profile.
customer_profile_id = "customer-1234"
body = talon_one.NewCustomerProfile(
    {"Name": "Anthony Ray", "ShippingCity": "Seattle", "ShippingRegion": "Washington"}
)
api_response = integration_api.update_customer_profile_v2(customer_profile_id, body)

# Create customer session payload
customer_session = talon_one.NewCustomerSessionV2()
# Add the profile id
# This is how we bind the session to the profile
customer_session.profile_id = customer_profile_id
# Create the request
integration_request = talon_one.IntegrationRequest(customer_session)
# Execute the request
api_response = integration_api.update_customer_session_v2(
    "my_session_id", integration_request
)
```

</TabItem>
<TabItem value="curl" label="curl">

 ```shell
CURL -X PUT https://mycompany.europe-west1.talon.one/v2/customer_sessions/my_session_id -d
'{
   "customerSession": {
      "profileId": "customer-1234",
      // cartItems ...
      // other session data if needed
   }
}'
```

</TabItem>
</Tabs>

## Close the session

We've seen how to create a session, bind it to a customer profile, and add custom
attributes to it. At the end of the order workflow, in our case the payment step, we have
to close the session. Doing so increments campaign budget limit counters, redeems potential
coupons, etc.

To close the session, we update the `state` property to `closed`:

<Tabs groupId="language">

<TabItem value="python" label="python">

```python title="Using the Python SDK to close a session"
# Reuse our previous customer_session object
# and update the state
customer_session.state = "closed"
# Create the request
integration_request = talon_one.IntegrationRequest(customer_session)
# Execute the request
api_response = integration_api.update_customer_session_v2(
    "my_session_id", integration_request
)
```

</TabItem>
<TabItem value="curl" label="curl">

 ```shell
CURL -X PUT https://mycompany.europe-west1.talon.one/v2/customer_sessions/my_session_id -d
'{
   "customerSession": {
      "state": "closed"
   }
}'
```

</TabItem>
</Tabs>

For more information about how to manage sessions, see [Managing session
states][sessionStates].

## Troubleshoot issues

You can display
the [Integration API logs](/docs/product/account/logs/integration-api-logs)
in the Campaign Manager to diagnose issues.

## Related pages

- [Integration checklist](/docs/dev/get-started/integration-checklist)
- [Integration API overview](/docs/dev/integration-api/overview)

[1]: /docs/dev/concepts/attributes#custom-attributes
[2]: /docs/dev/concepts/entities/customer-profiles
[3]: /docs/dev/concepts/entities/customer-sessions
[updateCustomerSession]: /integration-api#tag/Customer-sessions/operation/updateCustomerSessionV2
[sessionStates]: /docs/dev/concepts/entities/customer-sessions#customer-session-states
