package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
listID := uuid.MustParse("11111111-1111-4111-8111-111111111111")
// Record that the contact unsubscribed from this list. Setting confirmed
// would subscribe them again, so only do that when they asked for it.
response, _, err := client.ListsAPI.UpsertListContact(ctx, accountID, listID, "user+tag@example.com", requests.UpsertListContactRequest{
SubscriptionStatus: ahasend.String(requests.ListContactStatusUnsubscribed),
})
if err != nil {
log.Fatalf("Error updating the list membership: %v", err)
}
fmt.Printf("%s is %s\n", response.Email, response.SubscriptionStatus)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
const listId = "00000000-0000-4000-8000-000000000010";
const idOrEmail = "User+Tag@Example.COM";
const membership = await client.lists.contacts.upsert(listId, idOrEmail, {
subscription_status: "unsubscribed",
});
console.log("List membership saved.", { status: membership.subscription_status });
curl --request PUT \
--url https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}"
payload = {}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.put("https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"object": "list_contact",
"list_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"contact_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "jsmith@example.com",
"subscription_status": "unconfirmed",
"subscribed_at": "2023-11-07T05:31:56Z",
"unsubscribed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"contact": {
"object": "contact",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"email": "jsmith@example.com",
"first_name": "<string>",
"last_name": "<string>",
"status": "enabled",
"status_reason": "<string>",
"unsubscribed": true,
"unsubscribed_at": "2023-11-07T05:31:56Z",
"attributes": {},
"validation_status": "unvalidated",
"last_validated_at": "2023-11-07T05:31:56Z"
},
"list": {
"object": "contact_list",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"name": "<string>",
"description": "<string>",
"tags": [
"<string>"
]
}
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}Add or Update a List Contact via the API
Add an existing AhaSend contact to a list, or change its per-list subscription status to confirmed, unconfirmed or unsubscribed. No contact is created.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/AhaSend/ahasend-go"
"github.com/AhaSend/ahasend-go/api"
"github.com/AhaSend/ahasend-go/models/requests"
"github.com/google/uuid"
)
func main() {
client := api.NewAPIClient(api.WithAPIKey(os.Getenv("AHASEND_API_KEY")))
accountID := uuid.MustParse(os.Getenv("AHASEND_ACCOUNT_ID"))
ctx := context.Background()
listID := uuid.MustParse("11111111-1111-4111-8111-111111111111")
// Record that the contact unsubscribed from this list. Setting confirmed
// would subscribe them again, so only do that when they asked for it.
response, _, err := client.ListsAPI.UpsertListContact(ctx, accountID, listID, "user+tag@example.com", requests.UpsertListContactRequest{
SubscriptionStatus: ahasend.String(requests.ListContactStatusUnsubscribed),
})
if err != nil {
log.Fatalf("Error updating the list membership: %v", err)
}
fmt.Printf("%s is %s\n", response.Email, response.SubscriptionStatus)
}import { AhaSendClient } from "@ahasend/sdk";
const client = AhaSendClient.fromEnv();
const listId = "00000000-0000-4000-8000-000000000010";
const idOrEmail = "User+Tag@Example.COM";
const membership = await client.lists.contacts.upsert(listId, idOrEmail, {
subscription_status: "unsubscribed",
});
console.log("List membership saved.", { status: membership.subscription_status });
curl --request PUT \
--url https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}"
payload = {}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.put("https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ahasend.com/v2/accounts/{account_id}/lists/{list_id}/contacts/{id_or_email}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"object": "list_contact",
"list_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"contact_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "jsmith@example.com",
"subscription_status": "unconfirmed",
"subscribed_at": "2023-11-07T05:31:56Z",
"unsubscribed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"contact": {
"object": "contact",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"email": "jsmith@example.com",
"first_name": "<string>",
"last_name": "<string>",
"status": "enabled",
"status_reason": "<string>",
"unsubscribed": true,
"unsubscribed_at": "2023-11-07T05:31:56Z",
"attributes": {},
"validation_status": "unvalidated",
"last_validated_at": "2023-11-07T05:31:56Z"
},
"list": {
"object": "contact_list",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"name": "<string>",
"description": "<string>",
"tags": [
"<string>"
]
}
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}{
"message": "Error message"
}lists:write.
{id_or_email} is the contact ID or its email address. Percent-encode an email address exactly once. See Name a contact in the path. This operation does not create a contact. To create one, use Create Contact.
Status:
- The body can hold only
subscription_status:confirmed,unconfirmedorunsubscribed. An empty object is allowed. - A new membership is
confirmed, unless the body names another status. - For a membership that already exists, a body with no status changes nothing. To subscribe a contact again after an unsubscribe, name
confirmed. - When the body names the status that the membership already has, nothing changes,
updated_atincluded. So a retry gives the same result. confirmedsetssubscribed_atand clearsunsubscribed_at.unsubscribedsetsunsubscribed_atand keepssubscribed_at.unconfirmedsets neither.
complainedcannot be written. A body that names it is refused with400.- A membership that is
complainedcannot change. A request for it answers409and changes nothing, also a request with no status. - A list or a contact that does not exist answers
404, and the message says which.
Related Guides
- API authentication, Permission scopes, Batch Add List Contacts.
- Memberships, API errors, API rate limits.
Authorizations
API key for authentication. Non-empty Security Requirement values are AhaSend API-key roles. Roles listed within one requirement object are jointly required; separate requirement objects are alternatives.
Path Parameters
Account ID
List ID
Contact UUID or email address. Percent-encode an email exactly once as a path segment; SDK callers pass the raw email value.
Body
Names at most subscription_status. An empty object is legal and names
no status: on a new membership that means confirmed, and on one that
already exists it leaves the stored status exactly as it is. A property
that is not writable is rejected with 400 naming it.
Null and omission are one case and name no status. complained is
the mailbox provider's verdict rather than the caller's and is
rejected with 400.
confirmed, unconfirmed, unsubscribed, null Response
The membership, whether this request created it or updated one
One contact's membership of one list. It carries no id of its own: a
membership is addressed by the (list_id, contact_id) pair.
Object type identifier
list_contact The list this membership is on
The contact this membership belongs to
The contact's normalized email address
Per-list consent, independent of the contact's account-wide
unsubscribed flag. complained is the mailbox provider's verdict
and is never writable.
unconfirmed, confirmed, unsubscribed, complained When consent was given. Set when the membership becomes
confirmed and left alone afterwards, so it records when consent
was given rather than when it was last re-asserted.
When the membership became unsubscribed, or when the complaint
landed for a complained membership. Cleared when the membership
becomes confirmed again.
When the membership was created
When the membership was last updated
The whole contact. Present only on GET /v2/accounts/{account_id}/lists/{list_id}/contacts with
include_contacts=true.
Show child attributes
Show child attributes
The list this membership is on. Present only on GET /v2/accounts/{account_id}/contacts/{id_or_email}/lists.
Show child attributes
Show child attributes

