Customers

The Customers API allows you to manage your customer records. When a customer email is provided with a Quick Pay request, the API automatically creates or updates the customer record. You can also import, list, search, and soft-delete customers independently.


List customers

Returns a paginated list of customers. Supports searching by name or email, sorting, and date range filtering. Default pagination is 25 results per page (max 1000).

Request

GET /v2/customers

Query parameters

Parameter Type Description
searchstringFilter by name or email.
sortstringSort order. One of: created_asc, created_desc, first_name_asc, first_name_desc, last_name_asc, last_name_desc, last_order_asc, last_order_desc.
start_datestringStart date (Y-m-d).
end_datestringEnd date (Y-m-d).
per_pageintegerResults per page (default 25, max 1000).

Response

{
    "data": [
        {
            "id": "1d368e62",
            "first_name": "John",
            "last_name": "Testmore",
            "full_name": "John Testmore",
            "email": "[email protected]",
            "address": "1 Test Street, Test Town",
            "address_formatted": {
                "address_line_1": "1 Test Street",
                "address_line_2": null,
                "town": "Test Town",
                "postcode": null
            },
            "telephone": "0123456789",
            "marketing_consented_at": null,
            "created_at": "2023-05-02T22:07:21.000000Z",
            "updated_at": "2023-05-02T22:07:21.000000Z"
        }
    ],
    "links": {
        "first": "https://api.wonderful.one/v2/customers?page=1",
        "last": null,
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "path": "https://api.wonderful.one/v2/customers",
        "per_page": 25,
        "to": 1
    }
}

Example

curl --request GET \
    --get "https://api.wonderful.one/v2/customers?search=john&sort=last_name_asc&per_page=10" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL("https://api.wonderful.one/v2/customers");
const params = { search: "john", sort: "last_name_asc", per_page: "10" };
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

fetch(url, {
    headers: {
        "Authorization": "Bearer {YOUR_AUTH_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
})
    .then(response => response.json())
    .then(data => console.log(data.data));
$client = new \GuzzleHttp\Client();
$url = 'https://api.wonderful.one/v2/customers';
$response = $client->get($url, [
    'headers' => [
        'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
    ],
    'query' => [
        'search' => 'john',
        'sort' => 'last_name_asc',
        'per_page' => 10,
    ],
]);
$body = json_decode((string) $response->getBody());
print_r($body->data);

Create a customer

Inserts a new customer record. The email address is the unique identifier — creating a customer with an existing email updates the record instead of duplicating it.

Request

POST /v2/customers
Content-Type: application/json

Body parameters

Parameter Type Required Description
first_namestringnoMax 255 characters.
last_namestringnoMax 255 characters.
emailstringyesValid email address. Max 255 characters.
telephonestringnoPhone number.
marketing_consentbooleannotrue if the customer opted in to marketing.
addressstring or objectnoEither a single string (e.g. "123 Some Street, London, SW1A 1AA") or an object with address_line_1, address_line_2, town, postcode. When using an object, address_line_1 and postcode are required.

Response

{
    "data": {
        "id": "1d368e62",
        "first_name": "John",
        "last_name": "Smith",
        "full_name": "John Smith",
        "email": "[email protected]",
        "address": "123 Some Street, London, SW1A 1AA",
        "address_formatted": {
            "address_line_1": "123 Some Street",
            "address_line_2": null,
            "town": "London",
            "postcode": "SW1A 1AA"
        },
        "telephone": "0123456789",
        "marketing_consented_at": null,
        "created_at": "2024-03-05T11:50:43.000000Z",
        "updated_at": "2024-03-05T11:50:43.000000Z"
    }
}

Example

curl --request POST \
    "https://api.wonderful.one/v2/customers" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data '{
        "first_name": "John",
        "last_name": "Smith",
        "email": "[email protected]",
        "telephone": "01234 567890",
        "marketing_consent": true,
        "address": {
            "address_line_1": "123 Some Street",
            "town": "London",
            "postcode": "SW1A 1AA"
        }
    }'
fetch("https://api.wonderful.one/v2/customers", {
    method: "POST",
    headers: {
        "Authorization": "Bearer {YOUR_AUTH_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    body: JSON.stringify({
        first_name: "John",
        last_name: "Smith",
        email: "[email protected]",
        telephone: "01234 567890",
        marketing_consent: true,
        address: {
            address_line_1: "123 Some Street",
            town: "London",
            postcode: "SW1A 1AA",
        },
    }),
})
    .then(response => response.json())
    .then(data => console.log(data.data));
$client = new \GuzzleHttp\Client();
$response = $client->post('https://api.wonderful.one/v2/customers', [
    'headers' => [
        'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
    ],
    'json' => [
        'first_name' => 'John',
        'last_name' => 'Smith',
        'email' => '[email protected]',
        'telephone' => '01234 567890',
        'marketing_consent' => true,
        'address' => [
            'address_line_1' => '123 Some Street',
            'town' => 'London',
            'postcode' => 'SW1A 1AA',
        ],
    ],
]);
$body = json_decode((string) $response->getBody());
print_r($body->data);

Get a customer

Request

GET /v2/customers/{id}

Example

curl --request GET \
    --get "https://api.wonderful.one/v2/customers/1d368e62" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
fetch("https://api.wonderful.one/v2/customers/1d368e62", {
    headers: {
        "Authorization": "Bearer {YOUR_AUTH_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
})
    .then(response => response.json())
    .then(data => console.log(data.data));
$client = new \GuzzleHttp\Client();
$response = $client->get('https://api.wonderful.one/v2/customers/1d368e62', [
    'headers' => [
        'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
    ],
]);
$body = json_decode((string) $response->getBody());
print_r($body->data);

Update a customer

This is a full replacement — include all fields you want to keep, even if unchanged.

Request

PUT /v2/customers/{id}

Example

curl --request PUT \
    "https://api.wonderful.one/v2/customers/1d368e62" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data '{
        "first_name": "Jonathan",
        "last_name": "Smith",
        "email": "[email protected]",
        "telephone": "01234 567890"
    }'
fetch("https://api.wonderful.one/v2/customers/1d368e62", {
    method: "PUT",
    headers: {
        "Authorization": "Bearer {YOUR_AUTH_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    body: JSON.stringify({
        first_name: "Jonathan",
        last_name: "Smith",
        email: "[email protected]",
        telephone: "01234 567890",
    }),
})
    .then(response => response.json())
    .then(data => console.log(data.data));
$client = new \GuzzleHttp\Client();
$response = $client->put('https://api.wonderful.one/v2/customers/1d368e62', [
    'headers' => [
        'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
    ],
    'json' => [
        'first_name' => 'Jonathan',
        'last_name' => 'Smith',
        'email' => '[email protected]',
        'telephone' => '01234 567890',
    ],
]);
$body = json_decode((string) $response->getBody());
print_r($body->data);

Delete a customer

Soft-deletes the customer record. A successful delete returns 204 No Content. Attempting to delete an already-deleted record returns 404. Customers with existing orders cannot be deleted.

Request

DELETE /v2/customers/{id}

Example

curl --request DELETE \
    "https://api.wonderful.one/v2/customers/1d368e62" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
fetch("https://api.wonderful.one/v2/customers/1d368e62", {
    method: "DELETE",
    headers: {
        "Authorization": "Bearer {YOUR_AUTH_KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
});
$client = new \GuzzleHttp\Client();
$response = $client->delete('https://api.wonderful.one/v2/customers/1d368e62', [
    'headers' => [
        'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
        'Content-Type' => 'application/json',
        'Accept' => 'application/json',
    ],
]);

Marketing consent

The marketing_consented_at field indicates when a customer last opted in to marketing. A null value means they have not consented. Use this to segment your email lists:


Next steps

  • Orders & refunds — see how customer records are linked to orders.
  • Webhooks — receive real-time payment notifications.