Sending requests through the API is part of the Pro and Business plans. Reading works on every plan.
2. Make your first call GET /v1/me shows the account, the key and whether it can send. Every working key gets 200 here.
Answer
200 Copy {
"account": {
"id": "0b7c8f4e-5a1d-4c2e-9f3a-6d8e2b1c4a70",
"name": "Sunnyside Salon",
"plan": "pro",
"language": "en",
"time_zone": "America/New_York"
},
"key": {
"name": "Booking system",
"scopes": [
"read",
"send"
]
},
"can_send": true,
"credits": {
"balance": 180
}
}3. Send one review request POST /v1/requests sends to up to 100 customers at once. consent: true confirms the customers agreed to hear from you. With an Idempotency-Key, the same call within 24 hours goes out only once.
curl JavaScript Python
curl Copy curl -X POST "https://api.citypicked.com/v1/requests" \
-H "Authorization: Bearer cp_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: booking-1042" \
-d '{
"location_id": "5b1d0c7e-2f4a-4c3e-9a51-7d2e8f1b6a40",
"channel": "email",
"consent": true,
"contacts": [
{
"name": "Emma Clark",
"email": "emma@example.com"
}
]
}'JavaScript Copy const res = await fetch("https://api.citypicked.com/v1/requests", {
method: "POST",
headers: {
Authorization: "Bearer cp_live_xxxxxxxx",
"Content-Type": "application/json",
"Idempotency-Key": "booking-1042",
},
body: JSON.stringify({
"location_id": "5b1d0c7e-2f4a-4c3e-9a51-7d2e8f1b6a40",
"channel": "email",
"consent": true,
"contacts": [
{
"name": "Emma Clark",
"email": "emma@example.com"
}
]
}),
});
const data = await res.json();
console.log(res.status, data);Python Copy import requests
res = requests.post(
"https://api.citypicked.com/v1/requests",
headers={"Authorization": "Bearer cp_live_xxxxxxxx", "Idempotency-Key": "booking-1042"},
json={
"location_id": "5b1d0c7e-2f4a-4c3e-9a51-7d2e8f1b6a40",
"channel": "email",
"consent": True,
"contacts": [
{
"name": "Emma Clark",
"email": "emma@example.com",
},
],
},
)
print(res.status_code, res.json())Answer
201 Copy {
"results": [
{
"index": 0,
"outcome": "created",
"request_id": "c3f1a9e2-7b4d-4e8a-b6c2-1f9d3e5a7b20"
}
],
"created": 1
}created means the request is on its wayduplicate means this customer was asked recently. The window is 30 days unless the account changed it in the appopted_out, invalid, no_credits and consent_method_missing say why nothing went outFor SMS, use "channel": "sms" and a number like +1 201 555 0123. SMS to US and Canadian numbers also need consent_method and go out from the business's own number once the carriers have checked its business details.
4. Read the result Use the request_id from the answer to check where it stands. status moves from sent through delivered and clicked to rated, and rating shows the stars.
Answer
200 Copy {
"id": "c3f1a9e2-7b4d-4e8a-b6c2-1f9d3e5a7b20",
"location_id": "5b1d0c7e-2f4a-4c3e-9a51-7d2e8f1b6a40",
"channel": "email",
"source": "api",
"status": "rated",
"contact": {
"name": "Emma C.",
"email": "em***@example.com",
"phone": null
},
"rating": 5,
"created_at": "2026-11-08T09:30:00Z",
"sent_at": "2026-11-08T09:30:04Z",
"clicked_at": "2026-11-08T10:12:40Z",
"rated_at": "2026-11-08T10:13:05Z"
}Instead of asking again, you can have it sent to you. With webhooks every rating and every piece of feedback arrives at your URL right away.