# Contacts, segments & audiences

> Store contacts with typed properties, group them in segments for broadcasts, and keep unsubscribes in sync with the suppression list. Legacy audiences are supported.

Source: https://docs.pidgely.com/guides/contacts/

Contacts are the people you send [broadcasts](https://docs.pidgely.com/guides/broadcasts/) to:

- **Contacts are global** to your account and unique by email.
- **Segments** are named groups. A contact can belong to any number of segments.
- **Audiences** are the legacy name for segments, kept so older code keeps working.

## Contacts

```js tab="Node.js"
import { Pidgely } from 'pidgely';

const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);

const { data, error } = await pidgely.contacts.create({
  email: 'steve@example.com',
  firstName: 'Steve',
  lastName: 'Wozniak',
  properties: {
    plan: 'pro',
    seats: 5,
    beta: true,
  },
  segments: [
    {
      id: '0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48',
    },
  ],
});
console.log(data ?? error);
```

```python tab="Python"
import os
import pidgely

pidgely.api_key = os.environ["PIDGELY_API_KEY"]

result = pidgely.Contacts.create({
    "email": "steve@example.com",
    "first_name": "Steve",
    "last_name": "Wozniak",
    "properties": {
        "plan": "pro",
        "seats": 5,
        "beta": True,
    },
    "segments": [
        {
            "id": "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48",
        },
    ],
})
print(result)
```

```php tab="PHP"
<?php

require __DIR__ . '/vendor/autoload.php';

$pidgely = Pidgely::client(getenv('PIDGELY_API_KEY'));

$result = $pidgely->contacts->create([
    'email' => 'steve@example.com',
    'first_name' => 'Steve',
    'last_name' => 'Wozniak',
    'properties' => [
        'plan' => 'pro',
        'seats' => 5,
        'beta' => true,
    ],
    'segments' => [
        [
            'id' => '0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48',
        ],
    ],
]);
var_dump($result);
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/pidgely/pidgely-go"
)

func main() {
	ctx := context.Background()
	client := pidgely.NewClient(os.Getenv("PIDGELY_API_KEY"))

	result, err := client.Contacts.Create(ctx, &pidgely.CreateContactRequest{
		Email:      "steve@example.com",
		FirstName:  "Steve",
		LastName:   "Wozniak",
		Properties: map[string]any{
		"plan":  "pro",
		"seats": 5,
		"beta":  true,
	},
		Segments:   []pidgely.Segment{
		{
			Id: "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48",
		},
	},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
```

```ruby tab="Ruby"
require "pidgely"

Pidgely.api_key = ENV["PIDGELY_API_KEY"]

result = Pidgely::Contacts.create({
  email: "steve@example.com",
  first_name: "Steve",
  last_name: "Wozniak",
  properties: {
    plan: "pro",
    seats: 5,
    beta: true
  },
  segments: [
    {
      id: "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48"
    }
  ]
})
puts result
```

```bash tab="cURL"
curl -X POST 'https://api.pidgely.com/contacts' \
  -H "Authorization: Bearer $PIDGELY_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "steve@example.com",
  "first_name": "Steve",
  "last_name": "Wozniak",
  "properties": {
    "plan": "pro",
    "seats": 5,
    "beta": true
  },
  "segments": [
    {
      "id": "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48"
    }
  ]
}'
```

- [`POST /contacts`](https://docs.pidgely.com/api-reference/contacts/create/) is an **upsert**: posting an email that already exists updates that contact.
- `first_name` and `last_name` are up to 200 characters.
- **Properties**: up to 100 per contact. Keys match `^[A-Za-z_][A-Za-z0-9_]{0,49}$`; values are strings (up to 2,000 characters), numbers or booleans. Set a key to `null` to delete it. Reads return each property as `{ "value": …, "type": "string" | "number" | "boolean" }`.
- Retrieve, update and delete by **id or email**: `GET /contacts/steve@example.com` works.
- `topics` is not supported yet and is rejected.

## Unsubscribes and the suppression list

`unsubscribed` and the [suppression list](https://docs.pidgely.com/guides/suppressions/) stay in sync, both ways:

| Change | Effect |
|---|---|
| The contact clicks the unsubscribe link of a broadcast | An `unsubscribe` suppression is added and the contact becomes `unsubscribed: true` |
| You set `unsubscribed: true` | An `unsubscribe` suppression is added |
| You set `unsubscribed: false` | The `unsubscribe` suppression is removed (bounce and complaint suppressions are never removed this way) |
| You remove an `unsubscribe` suppression | The contact becomes subscribed again |
| You create a contact whose address already has an `unsubscribe` suppression | The contact starts as unsubscribed |

Suppressions are account-wide: an address that unsubscribed from broadcasts is also skipped by transactional sends. Deleting a contact keeps its suppression.

## Segments

Create a segment, then add contacts to it:

```js tab="Node.js"
import { Pidgely } from 'pidgely';

const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);

const { data, error } = await pidgely.segments.create({
  name: 'Newsletter',
});
console.log(data ?? error);
```

```python tab="Python"
import os
import pidgely

pidgely.api_key = os.environ["PIDGELY_API_KEY"]

result = pidgely.Segments.create({
    "name": "Newsletter",
})
print(result)
```

```php tab="PHP"
<?php

require __DIR__ . '/vendor/autoload.php';

$pidgely = Pidgely::client(getenv('PIDGELY_API_KEY'));

$result = $pidgely->segments->create([
    'name' => 'Newsletter',
]);
var_dump($result);
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/pidgely/pidgely-go"
)

func main() {
	ctx := context.Background()
	client := pidgely.NewClient(os.Getenv("PIDGELY_API_KEY"))

	result, err := client.Segments.Create(ctx, &pidgely.CreateSegmentRequest{
		Name: "Newsletter",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
```

```ruby tab="Ruby"
require "pidgely"

Pidgely.api_key = ENV["PIDGELY_API_KEY"]

result = Pidgely::Segments.create({
  name: "Newsletter"
})
puts result
```

```bash tab="cURL"
curl -X POST 'https://api.pidgely.com/segments' \
  -H "Authorization: Bearer $PIDGELY_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "Newsletter"
}'
```

```js tab="Node.js"
import { Pidgely } from 'pidgely';

const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);

const { data, error } = await pidgely.contacts.segments.add({
  email: 'steve@example.com',
  segmentId: '0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48',
});
console.log(data ?? error);
```

```python tab="Python"
import os
import pidgely

pidgely.api_key = os.environ["PIDGELY_API_KEY"]

result = pidgely.Contacts.Segments.add({
    "email": "steve@example.com",
    "segment_id": "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48",
})
print(result)
```

```php tab="PHP"
<?php

require __DIR__ . '/vendor/autoload.php';

$pidgely = Pidgely::client(getenv('PIDGELY_API_KEY'));

$result = $pidgely->contacts->segments->add([
    'email' => 'steve@example.com',
    'segment_id' => '0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48',
]);
var_dump($result);
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/pidgely/pidgely-go"
)

func main() {
	ctx := context.Background()
	client := pidgely.NewClient(os.Getenv("PIDGELY_API_KEY"))

	result, err := client.Contacts.Segments.Add(ctx, "steve@example.com", "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48")
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
```

```ruby tab="Ruby"
require "pidgely"

Pidgely.api_key = ENV["PIDGELY_API_KEY"]

result = Pidgely::Contacts::Segments.add({
  email: "steve@example.com",
  segment_id: "0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48"
})
puts result
```

```bash tab="cURL"
curl -X POST 'https://api.pidgely.com/contacts/steve@example.com/segments/0199d7f5-9a3e-7d4b-a1c2-7e5f0b3d6c48' \
  -H "Authorization: Bearer $PIDGELY_API_KEY"
```

- List a segment's members with [`GET /segments/{id}/contacts`](https://docs.pidgely.com/api-reference/segments/list-contacts/) or `GET /contacts?segment_id=…`.
- Removing a contact from a segment keeps the contact. Deleting a segment keeps its contacts.
- Segment names are 1–200 characters.

## Audiences (legacy)

The `/audiences` endpoints are aliases of segments over the same global contacts, and broadcasts accept `audience_id` as an alias of `segment_id`.

> [!WARNING]
> `DELETE /audiences/{id}/contacts/{email}` deletes the **whole contact**, not only its membership, because contacts are global. To remove someone from one segment only, use [`DELETE /contacts/{id}/segments/{segment_id}`](https://docs.pidgely.com/api-reference/contacts/remove-segment/).

## Webhooks

Contact changes emit `contact.created`, `contact.updated` and `contact.deleted` [webhooks](https://docs.pidgely.com/guides/webhooks/#events) with the contact's id, email, names, `unsubscribed` and timestamps.
