├── src ├── Region │ ├── InvalidRegionException.php │ ├── RegionUs.php │ ├── RegionEu.php │ └── RegionInterface.php ├── Endpoint │ ├── Activities.php │ ├── Events.php │ ├── Page.php │ ├── Messages.php │ ├── MessageTemplates.php │ ├── Send.php │ ├── SenderIdentities.php │ ├── Exports.php │ ├── Track.php │ ├── Customers │ │ └── Devices.php │ ├── Newsletters.php │ ├── Base.php │ ├── Collections.php │ ├── Segments.php │ ├── Campaigns.php │ └── Customers.php ├── Region.php └── Client.php ├── composer.json ├── LICENSE └── README.md /src/Region/InvalidRegionException.php: -------------------------------------------------------------------------------- 1 | activitiesPath(); 18 | 19 | return $this->client->get($path, $options); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Endpoint/Events.php: -------------------------------------------------------------------------------- 1 | mockException('Name is required!', 'POST'); 20 | } // @codeCoverageIgnore 21 | 22 | return $this->client->post("events", $options); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Region.php: -------------------------------------------------------------------------------- 1 | mockException('User id is required!', 'POST'); 20 | } // @codeCoverageIgnore 21 | 22 | if (!isset($options['url'])) { 23 | $this->mockException('URL is required!', 'POST'); 24 | } // @codeCoverageIgnore 25 | 26 | $options['name'] = $options['url']; 27 | unset($options['url']); 28 | 29 | $path = $this->customerPath($options['id'], ['events']); 30 | unset($options['id']); 31 | 32 | return $this->client->post($path, array_merge(["type" => "page"], $options)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "printu/customerio", 3 | "description": "PHP API for Customer.io", 4 | "keywords": [ 5 | "http", 6 | "rest", 7 | "customer.io", 8 | "customerio", 9 | "api", 10 | "client" 11 | ], 12 | "type": "library", 13 | "license": "MIT", 14 | "homepage": "http://customer.io/", 15 | "authors": [ 16 | { 17 | "name": "Paweł Krzaczkowski", 18 | "email": "pawel@freshmind.pl", 19 | "homepage": "https://github.com/krzaczek" 20 | } 21 | ], 22 | "minimum-stability": "stable", 23 | "require": { 24 | "php": "~7.3|^8.0", 25 | "ext-json": "*", 26 | "guzzlehttp/guzzle": "~7.2" 27 | }, 28 | "conflict": { 29 | "userscape/customerio": "*" 30 | }, 31 | "require-dev": { 32 | "phpunit/phpunit": "~9.5" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Customerio\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Customerio\\Tests\\": "tests/" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020 Printu Sp. z o. o., https://printu.pl 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/Endpoint/Messages.php: -------------------------------------------------------------------------------- 1 | messagesPath(); 19 | return $this->client->get($path, $options); 20 | } 21 | 22 | /** 23 | * Get metadata about messages 24 | * @see https://learn.customer.io/api/#apibeta-apimessagesmessages_get 25 | * @param array $options 26 | * @return mixed 27 | * @throws GuzzleException 28 | */ 29 | public function get(array $options) 30 | { 31 | if (!isset($options['id'])) { 32 | $this->mockException('Message id is required!', 'GET'); 33 | } // @codeCoverageIgnore 34 | 35 | $path = $this->messagesPath($options['id']); 36 | unset($options['id']); 37 | 38 | return $this->client->get($path, $options); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Endpoint/MessageTemplates.php: -------------------------------------------------------------------------------- 1 | mockException('Message Template id is required!', 'GET'); 20 | } // @codeCoverageIgnore 21 | 22 | $path = $this->messagesTemplatesPath($options['id']); 23 | unset($options['id']); 24 | 25 | return $this->client->get($path, $options); 26 | } 27 | 28 | /** 29 | * Get message template metrics 30 | * @see https://learn.customer.io/api/#apibeta-apimsg_templatesmsg_templates_get_metrics 31 | * @param array $options 32 | * @return mixed 33 | * @throws GuzzleException 34 | */ 35 | public function metrics(array $options) 36 | { 37 | if (!isset($options['id'])) { 38 | $this->mockException('Message Template id is required!', 'GET'); 39 | } // @codeCoverageIgnore 40 | 41 | $path = $this->messagesTemplatesPath($options['id'], ['metrics']); 42 | unset($options['id']); 43 | 44 | return $this->client->get($path, $options); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Endpoint/Send.php: -------------------------------------------------------------------------------- 1 | mockException('Email body is required if transactional_message_id is missing!', 'POST'); 21 | } // @codeCoverageIgnore 22 | 23 | if (!isset($options['subject'])) { 24 | $this->mockException('Email subject is required if transactional_message_id is missing!', 'POST'); 25 | } // @codeCoverageIgnore 26 | 27 | if (!isset($options['from'])) { 28 | $this->mockException('Email from is required if transactional_message_id is missing!', 'POST'); 29 | } // @codeCoverageIgnore 30 | } 31 | 32 | if (!isset($options['identifiers'])) { 33 | $this->mockException('Email identifiers is required!', 'POST'); 34 | } // @codeCoverageIgnore 35 | 36 | if (!isset($options['to'])) { 37 | $this->mockException('Email to is required!', 'POST'); 38 | } // @codeCoverageIgnore 39 | 40 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 41 | 42 | return $this->client->post('send/email', $options); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Endpoint/SenderIdentities.php: -------------------------------------------------------------------------------- 1 | senderIdentitiesPath(); 18 | 19 | return $this->client->get($path, $options); 20 | } 21 | 22 | /** 23 | * Get sender identity data 24 | * @param array $options 25 | * @return mixed 26 | * @throws GuzzleException 27 | */ 28 | public function get(array $options) 29 | { 30 | if (!isset($options['id'])) { 31 | $this->mockException('The unique id of the sender identity is required!', 'GET'); 32 | } // @codeCoverageIgnore 33 | 34 | $path = $this->senderIdentitiesPath($options['id']); 35 | unset($options['id']); 36 | 37 | return $this->client->get($path, $options); 38 | } 39 | 40 | /** 41 | * Get sender identity usage data. 42 | * @param array $options 43 | * @return mixed 44 | * @throws GuzzleException 45 | */ 46 | public function usedBy(array $options) 47 | { 48 | if (!isset($options['id'])) { 49 | $this->mockException('The unique id of the sender identity is required!', 'GET'); 50 | } // @codeCoverageIgnore 51 | 52 | $path = $this->senderIdentitiesPath($options['id'], ['used_by']); 53 | unset($options['id']); 54 | 55 | return $this->client->get($path, $options); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Endpoint/Exports.php: -------------------------------------------------------------------------------- 1 | exportsPath(); 19 | return $this->client->get($path, $options); 20 | } 21 | 22 | /** 23 | * Get export data 24 | * @see https://learn.customer.io/api/#apibeta-apiexportsexports_get 25 | * @param array $options 26 | * @return mixed 27 | * @throws GuzzleException 28 | */ 29 | public function get(array $options) 30 | { 31 | if (!isset($options['id'])) { 32 | $this->mockException('Exports id is required!', 'GET'); 33 | } // @codeCoverageIgnore 34 | 35 | $path = $this->exportsPath($options['id']); 36 | unset($options['id']); 37 | 38 | return $this->client->get($path, $options); 39 | } 40 | 41 | /** 42 | * Download an export 43 | * @see https://learn.customer.io/api/#apibeta-apiexportsexports_download 44 | * @param array $options 45 | * @return mixed 46 | * @throws GuzzleException 47 | */ 48 | public function download(array $options) 49 | { 50 | if (!isset($options['id'])) { 51 | $this->mockException('Exports id is required!', 'GET'); 52 | } // @codeCoverageIgnore 53 | 54 | $path = $this->exportsPath($options['id'], ['download']); 55 | unset($options['id']); 56 | 57 | return $this->client->get($path, $options); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Endpoint/Track.php: -------------------------------------------------------------------------------- 1 | mockException('Operation type is required!', 'POST'); 20 | } // @codeCoverageIgnore 21 | 22 | if (!isset($options['action'])) { 23 | $this->mockException('An event action is required!', 'POST'); 24 | } // @codeCoverageIgnore 25 | 26 | if (!isset($options['identifiers'])) { 27 | $this->mockException('Object identifiers is required!', 'POST'); 28 | } // @codeCoverageIgnore 29 | 30 | $path = $this->generatePath('entity'); 31 | $options['endpoint'] = $this->client->getRegion()->trackUri('v2'); 32 | 33 | return $this->client->post($path, $options); 34 | } 35 | 36 | /** 37 | * Send multiple requests 38 | * @see https://docs.customer.io/api/track/#operation/batch 39 | * @param array $options 40 | * @return mixed 41 | * @throws GuzzleException 42 | */ 43 | public function batch(array $options) 44 | { 45 | if (!isset($options['batch'])) { 46 | $this->mockException('Batch paremeter is required!', 'POST'); 47 | } // @codeCoverageIgnore 48 | 49 | $path = $this->generatePath('batch'); 50 | $options['endpoint'] = $this->client->getRegion()->trackUri('v2'); 51 | 52 | return $this->client->post($path, $options); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Endpoint/Customers/Devices.php: -------------------------------------------------------------------------------- 1 | mockException('User id is required!', 'PUT'); 20 | } // @codeCoverageIgnore 21 | 22 | if (!isset($options['device']['id'])) { 23 | $this->mockException('Device id is required!', 'PUT'); 24 | } // @codeCoverageIgnore 25 | 26 | if (!isset($options['device']['platform'])) { 27 | $this->mockException('Device platform is required!', 'PUT'); 28 | } // @codeCoverageIgnore 29 | 30 | $path = $this->customerPath($options['id'], ['devices']); 31 | 32 | return $this->client->put($path, $options); 33 | } 34 | 35 | /** 36 | * Delete device 37 | * @param array $options 38 | * @return mixed 39 | * @throws GuzzleException 40 | */ 41 | public function delete(array $options) 42 | { 43 | if (!isset($options['id'])) { 44 | $this->mockException('User id is required!', 'DELETE'); 45 | } // @codeCoverageIgnore 46 | 47 | if (!isset($options['device_id'])) { 48 | $this->mockException('Device id is required!', 'DELETE'); 49 | } // @codeCoverageIgnore 50 | 51 | $path = $this->customerPath($options['id'], ['devices', $options['device_id']]); 52 | 53 | return $this->client->delete($path, []); 54 | } 55 | 56 | /** 57 | * Update device 58 | * @param array $options 59 | * @return mixed 60 | * @throws GuzzleException 61 | */ 62 | public function update(array $options) 63 | { 64 | return $this->add($options); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Endpoint/Newsletters.php: -------------------------------------------------------------------------------- 1 | newslettersPath(); 19 | return $this->client->get($path, $options); 20 | } 21 | 22 | /** 23 | * Get newsletter data 24 | * @see https://learn.customer.io/api/#apibeta-apinewslettersnewsletters_get 25 | * @param array $options 26 | * @return mixed 27 | * @throws GuzzleException 28 | */ 29 | public function get(array $options) 30 | { 31 | if (!isset($options['id'])) { 32 | $this->mockException('Newsletter id is required!', 'GET'); 33 | } // @codeCoverageIgnore 34 | 35 | $path = $this->newslettersPath($options['id']); 36 | unset($options['id']); 37 | 38 | return $this->client->get($path, $options); 39 | } 40 | 41 | /** 42 | * Get newsletter metrics 43 | * @see https://learn.customer.io/api/#apibeta-apinewslettersnewsletters_get_metrics 44 | * @param array $options 45 | * @return mixed 46 | * @throws GuzzleException 47 | */ 48 | public function metrics(array $options) 49 | { 50 | if (!isset($options['id'])) { 51 | $this->mockException('Newsletter id is required!', 'GET'); 52 | } // @codeCoverageIgnore 53 | 54 | $path = $this->newslettersPath($options['id'], ['metrics']); 55 | unset($options['id']); 56 | 57 | return $this->client->get($path, $options); 58 | } 59 | 60 | /** 61 | * Get metadata about messages sent by a newsletter 62 | * @see https://learn.customer.io/api/#apibeta-apinewslettersnewsletters_messages 63 | * @param array $options 64 | * @return mixed 65 | * @throws GuzzleException 66 | */ 67 | public function messages(array $options) 68 | { 69 | if (!isset($options['id'])) { 70 | $this->mockException('Newsletter id is required!', 'GET'); 71 | } // @codeCoverageIgnore 72 | 73 | $path = $this->newslettersPath($options['id'], ['messages']); 74 | unset($options['id']); 75 | 76 | return $this->client->get($path, $options); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Endpoint/Base.php: -------------------------------------------------------------------------------- 1 | client = $client; 22 | } 23 | 24 | /** 25 | * @param null $id 26 | * @param array $extra 27 | * @return string 28 | */ 29 | public function customerPath($id = null, array $extra = []): string 30 | { 31 | return $this->generatePath('customers', $id, $extra); 32 | } 33 | 34 | /** 35 | * @param null $id 36 | * @param array $extra 37 | * @return string 38 | */ 39 | public function campaignPath($id = null, array $extra = []): string 40 | { 41 | return $this->generatePath('campaigns', $id, $extra); 42 | } 43 | 44 | /** 45 | * @param null $id 46 | * @param array $extra 47 | * @return string 48 | */ 49 | public function messagesPath($id = null, array $extra = []): string 50 | { 51 | return $this->generatePath('messages', $id, $extra); 52 | } 53 | 54 | /** 55 | * @param $id 56 | * @param array $extra 57 | * @return string 58 | */ 59 | public function messagesTemplatesPath($id, array $extra = []): string 60 | { 61 | return $this->generatePath('msg_templates', $id, $extra); 62 | } 63 | 64 | /** 65 | * @param int $id 66 | * @param array $extra 67 | * @return string 68 | */ 69 | public function newslettersPath($id = null, array $extra = []): string 70 | { 71 | return $this->generatePath('newsletters', $id, $extra); 72 | } 73 | 74 | /** 75 | * @param int $id 76 | * @param array $extra 77 | * @return string 78 | */ 79 | public function segmentsPath($id = null, array $extra = []): string 80 | { 81 | return $this->generatePath('segments', $id, $extra); 82 | } 83 | 84 | /** 85 | * @param int $id 86 | * @param array $extra 87 | * @return string 88 | */ 89 | public function exportsPath($id = null, array $extra = []): string 90 | { 91 | return $this->generatePath('exports', $id, $extra); 92 | } 93 | 94 | /** 95 | * @param int $id 96 | * @param array $extra 97 | * @return string 98 | */ 99 | public function activitiesPath($id = null, array $extra = []): string 100 | { 101 | return $this->generatePath('activities', $id, $extra); 102 | } 103 | 104 | /** 105 | * @param int|null $id 106 | * @param array $extras 107 | * @return string 108 | */ 109 | public function collectionsPath(?int $id = null, array $extras = []): string 110 | { 111 | return $this->generatePath('collections', $id, $extras); 112 | } 113 | 114 | /** 115 | * @param null $id 116 | * @param array $extra 117 | * @return string 118 | */ 119 | public function senderIdentitiesPath($id = null, array $extra = []): string 120 | { 121 | return $this->generatePath('sender_identities', $id, $extra); 122 | } 123 | 124 | /** 125 | * @param $prefix 126 | * @param null $id 127 | * @param array $extra 128 | * @return string 129 | */ 130 | public function generatePath($prefix, $id = null, array $extra = []): string 131 | { 132 | $path = [ 133 | $prefix, 134 | ]; 135 | 136 | if (!empty($id)) { 137 | $path[] = (string)$id; 138 | } 139 | 140 | if (!empty($extra)) { 141 | $path = array_merge($path, $extra); 142 | } 143 | 144 | return implode('/', $path); 145 | } 146 | 147 | /** 148 | * @param $message 149 | * @param $method 150 | */ 151 | protected function mockException($message, $method): RequestExceptionInterface 152 | { 153 | throw new RequestException($message, (new Request($method, '/'))); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Endpoint/Collections.php: -------------------------------------------------------------------------------- 1 | collectionsPath(); 20 | 21 | return $this->client->get($path, $options); 22 | } 23 | 24 | /** 25 | * Get collection 26 | * @see https://customer.io/docs/api/#operation/getCollection 27 | * @param array $options 28 | * @return mixed 29 | * @throws GuzzleException 30 | */ 31 | public function get(array $options) 32 | { 33 | if (!isset($options['id'])) { 34 | $this->mockException('Collection id is required!', 'GET'); 35 | } // @codeCoverageIgnore 36 | 37 | $path = $this->collectionsPath($options['id']); 38 | unset($options['id']); 39 | 40 | return $this->client->get($path, $options); 41 | } 42 | 43 | /** 44 | * Create collection 45 | * @see https://customer.io/docs/api/#operation/addCollection 46 | * @param array $options 47 | * @return mixed 48 | * @throws GuzzleException 49 | */ 50 | public function create(array $options) 51 | { 52 | if (!isset($options['name'])) { 53 | $this->mockException('Collection name is required!', 'POST'); 54 | } 55 | 56 | if (!isset($options['data']) && !isset($options['url'])) { 57 | $this->mockException('Collection data or url is required!', 'POST'); 58 | } // @codeCoverageIgnore 59 | 60 | $path = $this->collectionsPath(); 61 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 62 | 63 | return $this->client->post($path, $options); 64 | } 65 | 66 | /** 67 | * Delete a collection 68 | * @see https://customer.io/docs/api/#operation/deleteCollection 69 | * @param array $options 70 | * @return mixed 71 | * @throws GuzzleException 72 | */ 73 | public function delete(array $options) 74 | { 75 | if (!isset($options['collection_id'])) { 76 | $this->mockException('Collection collection_id is required!', 'POST'); 77 | } 78 | 79 | $path = $this->collectionsPath($options['collection_id']); 80 | unset($options['collection_id']); 81 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 82 | 83 | return $this->client->delete($path, $options); 84 | } 85 | 86 | /** 87 | * Update a collection name or data 88 | * @see https://customer.io/docs/api/#operation/updateCollection 89 | * @param array $options 90 | * @return mixed 91 | * @throws GuzzleException 92 | */ 93 | public function update(array $options) 94 | { 95 | if (!isset($options['collection_id'])) { 96 | $this->mockException('Collection collection_id is required!', 'POST'); 97 | } 98 | 99 | $path = $this->collectionsPath($options['collection_id']); 100 | unset($options['collection_id']); 101 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 102 | 103 | return $this->client->put($path, $options); 104 | } 105 | 106 | /** 107 | * Get collection content 108 | * @see https://customer.io/docs/api/#operation/getCollectionContents 109 | * @param array $options 110 | * @return mixed 111 | * @throws GuzzleException 112 | */ 113 | public function content(array $options) 114 | { 115 | if (!isset($options['collection_id'])) { 116 | $this->mockException('Collection collection_id is required!', 'POST'); 117 | } 118 | 119 | $path = $this->collectionsPath($options['collection_id'], ['content']); 120 | unset($options['collection_id']); 121 | $options['raw'] = true; 122 | 123 | return $this->client->get($path, $options); 124 | } 125 | 126 | /** 127 | * Update collection data 128 | * @see https://customer.io/docs/api/#operation/updateCollectionContents 129 | * @param array $options 130 | * @return mixed 131 | * @throws GuzzleException 132 | */ 133 | public function updateContent(array $options) 134 | { 135 | if (!isset($options['collection_id'])) { 136 | $this->mockException('Collection collection_id is required!', 'POST'); 137 | } 138 | 139 | if (!isset($options['data'])) { 140 | $this->mockException('Collection data is required!', 'POST'); 141 | } // @codeCoverageIgnore 142 | 143 | $path = $this->collectionsPath($options['collection_id'], ['content']); 144 | $options = $options['data']; 145 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 146 | 147 | return $this->client->put($path, $options); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Endpoint/Segments.php: -------------------------------------------------------------------------------- 1 | mockException('Segments name is required!', 'POST'); 21 | } // @codeCoverageIgnore 22 | 23 | $path = $this->segmentsPath(); 24 | $json = ['segment' => $options]; 25 | $json['endpoint'] = $this->client->getRegion()->apiUri(); 26 | 27 | return $this->client->post($path, $json); 28 | } 29 | 30 | /** 31 | * List segments 32 | * @see https://learn.customer.io/api/#apibeta-apisegmentssegments_list 33 | * @param array $options 34 | * @return mixed 35 | * @throws GuzzleException 36 | */ 37 | public function search(array $options) 38 | { 39 | $path = $this->segmentsPath(); 40 | 41 | return $this->client->get($path, $options); 42 | } 43 | 44 | /** 45 | * Get segment data 46 | * @see https://learn.customer.io/api/#apibeta-apisegmentssegments_get 47 | * @param array $options 48 | * @return mixed 49 | * @throws GuzzleException 50 | */ 51 | public function get(array $options) 52 | { 53 | if (!isset($options['id'])) { 54 | $this->mockException('Segments id is required!', 'GET'); 55 | } // @codeCoverageIgnore 56 | 57 | $path = $this->segmentsPath($options['id']); 58 | unset($options['id']); 59 | 60 | return $this->client->get($path, $options); 61 | } 62 | 63 | /** 64 | * Get dependencies of a segment 65 | * @see https://learn.customer.io/api/#apibeta-apisegmentssegments_used_by 66 | * @param array $options 67 | * @return mixed 68 | * @throws GuzzleException 69 | */ 70 | public function usedBy(array $options) 71 | { 72 | if (!isset($options['id'])) { 73 | $this->mockException('Segments id is required!', 'GET'); 74 | } // @codeCoverageIgnore 75 | 76 | $path = $this->segmentsPath($options['id'], ['used_by']); 77 | unset($options['id']); 78 | 79 | return $this->client->get($path, $options); 80 | } 81 | 82 | /** 83 | * Get number of customers in a segment 84 | * @see https://learn.customer.io/api/#apibeta-apisegmentssegments_customer_count 85 | * @param array $options 86 | * @return mixed 87 | * @throws GuzzleException 88 | */ 89 | public function customerCount(array $options) 90 | { 91 | if (!isset($options['id'])) { 92 | $this->mockException('Segments id is required!', 'GET'); 93 | } // @codeCoverageIgnore 94 | 95 | $path = $this->segmentsPath($options['id'], ['customer_count']); 96 | unset($options['id']); 97 | 98 | return $this->client->get($path, $options); 99 | } 100 | 101 | /** 102 | * Get the membership of a segment 103 | * @see https://learn.customer.io/api/#apibeta-apisegmentssegments_customer_count 104 | * @param array $options 105 | * @return mixed 106 | * @throws GuzzleException 107 | */ 108 | public function membership(array $options) 109 | { 110 | if (!isset($options['id'])) { 111 | $this->mockException('Segments id is required!', 'GET'); 112 | } // @codeCoverageIgnore 113 | 114 | $path = $this->segmentsPath($options['id'], ['membership']); 115 | unset($options['id']); 116 | 117 | return $this->client->get($path, $options); 118 | } 119 | 120 | /** 121 | * Delete an existing manual segment by id 122 | * @see https://customer.io/docs/api/#apibeta-apisegmentssegment_delete 123 | * @param array $options 124 | * @return mixed 125 | * @throws GuzzleException 126 | */ 127 | public function delete(array $options) 128 | { 129 | if (!isset($options['id'])) { 130 | $this->mockException('Segments id is required!', 'DELETE'); 131 | } // @codeCoverageIgnore 132 | 133 | $path = $this->segmentsPath($options['id']); 134 | unset($options['id']); 135 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 136 | 137 | return $this->client->delete($path, $options); 138 | } 139 | 140 | /** 141 | * Add people to a manual segment 142 | * @see https://customer.io/docs/api/#apitracksegmentsadd_customers 143 | * @param array $options 144 | * @return mixed 145 | * @throws GuzzleException 146 | */ 147 | public function addCustomers(array $options) 148 | { 149 | if (!isset($options['id'])) { 150 | $this->mockException('Segments id is required!', 'POST'); 151 | } // @codeCoverageIgnore 152 | 153 | if (!isset($options['ids']) || !is_array($options['ids'])) { 154 | $this->mockException('Customer ids are required!', 'POST'); 155 | } // @codeCoverageIgnores 156 | 157 | $path = $this->segmentsPath($options['id'], ['add_customers']); 158 | unset($options['id']); 159 | 160 | return $this->client->post($path, $options); 161 | } 162 | 163 | /** 164 | * Remove people from a manual segment 165 | * @see https://customer.io/docs/api/#apitracksegmentsremove_customers 166 | * @param array $options 167 | * @return mixed 168 | * @throws GuzzleException 169 | */ 170 | public function removeCustomers(array $options) 171 | { 172 | if (!isset($options['id'])) { 173 | $this->mockException('Segments id is required!', 'POST'); 174 | } // @codeCoverageIgnore 175 | 176 | if (!isset($options['ids']) || !is_array($options['ids'])) { 177 | $this->mockException('Customer ids are required!', 'POST'); 178 | } // @codeCoverageIgnores 179 | 180 | $path = $this->segmentsPath($options['id'], ['remove_customers']); 181 | unset($options['id']); 182 | 183 | return $this->client->post($path, $options); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Endpoint/Campaigns.php: -------------------------------------------------------------------------------- 1 | campaignPath(); 19 | 20 | return $this->client->get($path, $options); 21 | } 22 | 23 | /** 24 | * List campaigns 25 | * @see https://learn.customer.io/api/#apibeta-apicampaignscampaigns_get 26 | * @param array $options 27 | * @return mixed 28 | * @throws GuzzleException 29 | */ 30 | public function get(array $options) 31 | { 32 | if (!isset($options['id'])) { 33 | $this->mockException('Campaign id is required!', 'GET'); 34 | } // @codeCoverageIgnore 35 | 36 | $path = $this->campaignPath($options['id']); 37 | unset($options['id']); 38 | 39 | return $this->client->get($path, $options); 40 | } 41 | 42 | /** 43 | * Get campaign metrics 44 | * @see https://learn.customer.io/api/#apibeta-apicampaignscampaigns_get_metrics 45 | * @see https://learn.customer.io/api/#apibeta-apicampaignscampaigns_get_triggers_metrics 46 | * @param array $options 47 | * @return mixed 48 | * @throws GuzzleException 49 | */ 50 | public function metrics(array $options) 51 | { 52 | if (!isset($options['id'])) { 53 | $this->mockException('Campaign id is required!', 'GET'); 54 | } // @codeCoverageIgnore 55 | 56 | $extra = []; 57 | if (isset($options['trigger_id'])) { 58 | $extra[] = 'triggers'; 59 | $extra[] = $options['trigger_id']; 60 | unset($options['trigger_id']); 61 | } 62 | $extra[] = 'metrics'; 63 | $path = $this->campaignPath($options['id'], $extra); 64 | unset($options['id']); 65 | 66 | return $this->client->get($path, $options); 67 | } 68 | 69 | /** 70 | * Get triggered campaigns 71 | * @see https://learn.customer.io/api/#apibeta-apicampaignscampaigns_get_triggers 72 | * @param array $options 73 | * @return mixed 74 | * @throws GuzzleException 75 | */ 76 | public function triggers(array $options) 77 | { 78 | if (!isset($options['id'])) { 79 | $this->mockException('Campaign id is required!', 'GET'); 80 | } // @codeCoverageIgnore 81 | 82 | $path = $this->campaignPath($options['id'], ['triggers']); 83 | unset($options['id']); 84 | 85 | return $this->client->get($path, $options); 86 | } 87 | 88 | /** 89 | * Trigger Campaign Broadcast 90 | * @see https://learn.customer.io/api/#apicorecampaignscampaigns_trigger 91 | * @see https://learn.customer.io/documentation/api-triggered-data-format.html 92 | * @param array $options 93 | * @return mixed 94 | * @throws GuzzleException 95 | */ 96 | public function trigger(array $options) 97 | { 98 | if (!isset($options['id'])) { 99 | $this->mockException('Campaign id is required!', 'POST'); 100 | } // @codeCoverageIgnore 101 | 102 | $path = $this->campaignPath($options['id'], ['triggers']); 103 | unset($options['id']); 104 | 105 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 106 | 107 | return $this->client->post($path, $options); 108 | } 109 | 110 | /** 111 | * Get triggered campaigns 112 | * @see https://learn.customer.io/api/#apibeta-apicampaignscampaigns_messages 113 | * @param array $options 114 | * @return mixed 115 | * @throws GuzzleException 116 | */ 117 | public function messages(array $options) 118 | { 119 | if (!isset($options['id'])) { 120 | $this->mockException('Campaign id is required!', 'GET'); 121 | } // @codeCoverageIgnore 122 | 123 | $path = $this->campaignPath($options['id'], ['messages']); 124 | unset($options['id']); 125 | 126 | return $this->client->get($path, $options); 127 | } 128 | 129 | /** 130 | * List campaign actions 131 | * @see https://customer.io/docs/api/#apibeta-apicampaignscampaigns_index_actions 132 | * @param array $options 133 | * @return mixed 134 | * @throws GuzzleException 135 | */ 136 | public function actions(array $options) 137 | { 138 | if (!isset($options['id'])) { 139 | $this->mockException('Campaign id is required!', 'GET'); 140 | } // @codeCoverageIgnore 141 | 142 | $path = $this->campaignPath($options['id'], ['actions']); 143 | unset($options['id']); 144 | 145 | return $this->client->get($path, $options); 146 | } 147 | 148 | /** 149 | * Get action of a campaign by id. 150 | * @see https://customer.io/docs/api/#apibeta-apicampaignscampaigns_get_action 151 | * @param array $options 152 | * @return mixed 153 | * @throws GuzzleException 154 | */ 155 | public function getAction(array $options) 156 | { 157 | if (!isset($options['id'])) { 158 | $this->mockException('Campaign id is required!', 'GET'); 159 | } // @codeCoverageIgn 160 | 161 | if (!isset($options['action_id'])) { 162 | $this->mockException('Action id is required!', 'GET'); 163 | } // @codeCoverageIgn 164 | 165 | $path = $this->campaignPath($options['id'], ['actions', $options['action_id']]); 166 | unset($options['id']); 167 | unset($options['action_id']); 168 | 169 | return $this->client->get($path, $options); 170 | } 171 | 172 | /** 173 | * Get action metrics of a campaign 174 | * @see https://customer.io/docs/api/#apibeta-apicampaignscampaign_action_metrics 175 | * @param array $options 176 | * @return mixed 177 | * @throws GuzzleException 178 | */ 179 | public function getActionMetrics(array $options) 180 | { 181 | if (!isset($options['id'])) { 182 | $this->mockException('Campaign id is required!', 'GET'); 183 | } // @codeCoverageIgn 184 | 185 | if (!isset($options['action_id'])) { 186 | $this->mockException('Action id is required!', 'GET'); 187 | } // @codeCoverageIgn 188 | 189 | $path = $this->campaignPath($options['id'], ['actions', $options['action_id'], 'metrics']); 190 | unset($options['id']); 191 | unset($options['action_id']); 192 | 193 | return $this->client->get($path, $options); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/Endpoint/Customers.php: -------------------------------------------------------------------------------- 1 | devices = new Customers\Devices($client); 20 | } 21 | 22 | /** 23 | * Register customer event 24 | * @param array $options 25 | * @return mixed 26 | * @throws GuzzleException 27 | */ 28 | public function event(array $options) 29 | { 30 | if (!isset($options['id']) && !isset($options['email']) && !isset($options['cio_id'])) { 31 | $this->mockException('User id, email or cio_id is required!', 'POST'); 32 | } // @codeCoverageIgnore 33 | 34 | $path = $this->setCustomerPathWithIdentifier($options); 35 | 36 | return $this->client->post($path."/events", $options); 37 | } 38 | 39 | /** 40 | * Add new customer 41 | * @param array $options 42 | * @return mixed 43 | * @throws GuzzleException 44 | */ 45 | public function add(array $options) 46 | { 47 | if (!isset($options['id']) && !isset($options['email']) && !isset($options['cio_id'])) { 48 | $this->mockException('User id or email is required!', 'PUT'); 49 | } // @codeCoverageIgnore 50 | 51 | $path = $this->setCustomerPathWithIdentifier($options); 52 | 53 | return $this->client->put($path, $options); 54 | } 55 | 56 | 57 | /** 58 | * Delete customer 59 | * @param array $options 60 | * @return mixed 61 | * @throws GuzzleException 62 | */ 63 | public function delete(array $options) 64 | { 65 | if (!isset($options['id']) && !isset($options['email']) && !isset($options['cio_id'])) { 66 | $this->mockException('User id or email is required!', 'DELETE'); 67 | } // @codeCoverageIgnore 68 | 69 | $path = $this->setCustomerPathWithIdentifier($options); 70 | 71 | return $this->client->delete($path, []); 72 | } 73 | 74 | /** 75 | * Update new customer 76 | * @param array $options 77 | * @return mixed 78 | * @throws GuzzleException 79 | */ 80 | public function update(array $options) 81 | { 82 | return $this->add($options); 83 | } 84 | 85 | /** 86 | * Get customer by email address 87 | * @param array $options 88 | * @return mixed 89 | * @throws GuzzleException 90 | */ 91 | public function get(array $options) 92 | { 93 | if (!isset($options['email'])) { 94 | $this->mockException('Email is required!', 'GET'); 95 | } // @codeCoverageIgnore 96 | 97 | $path = $this->customerPath(); 98 | 99 | return $this->client->get($path, $options); 100 | } 101 | 102 | /** 103 | * Search customers 104 | * @param array $options 105 | * @return mixed 106 | * @throws GuzzleException 107 | */ 108 | public function search(array $options) 109 | { 110 | if (!isset($options['filter'])) { 111 | $this->mockException('Filter is required!', 'POST'); 112 | } // @codeCoverageIgnore 113 | 114 | $path = $this->customerPath(); 115 | $options['endpoint'] = $this->client->getRegion()->apiUri(); 116 | 117 | return $this->client->post($path, $options); 118 | } 119 | 120 | /** 121 | * List customer attributes 122 | * @param array $options 123 | * @return mixed 124 | * @throws GuzzleException 125 | */ 126 | public function attributes(array $options) 127 | { 128 | if (!isset($options['id']) && !isset($options['email'])) { 129 | $this->mockException('User id or email is required!', 'GET'); 130 | } // @codeCoverageIgnore 131 | 132 | $path = $this->setCustomerPathWithIdentifier($options, ['attributes']); 133 | 134 | return $this->client->get($path, $options); 135 | } 136 | 137 | /** 138 | * List customer segments 139 | * @param array $options 140 | * @return mixed 141 | * @throws GuzzleException 142 | */ 143 | public function segments(array $options) 144 | { 145 | if (!isset($options['id']) && !isset($options['email'])) { 146 | $this->mockException('User id or email is required!', 'GET'); 147 | } // @codeCoverageIgnore 148 | 149 | $path = $this->setCustomerPathWithIdentifier($options, ['segments']); 150 | 151 | return $this->client->get($path, $options); 152 | } 153 | 154 | /** 155 | * Get metadata about messages sent to a customer 156 | * @param array $options 157 | * @return mixed 158 | * @throws GuzzleException 159 | */ 160 | public function messages(array $options) 161 | { 162 | if (!isset($options['id']) && !isset($options['email'])) { 163 | $this->mockException('User id or email is required!', 'GET'); 164 | } // @codeCoverageIgnore 165 | 166 | $path = $this->setCustomerPathWithIdentifier($options, ['messages']); 167 | 168 | return $this->client->get($path, $options); 169 | } 170 | 171 | /** 172 | * Get data about activities performed by or for a customer 173 | * @param array $options 174 | * @return mixed 175 | * @throws GuzzleException 176 | */ 177 | public function activities(array $options) 178 | { 179 | if (!isset($options['id']) && !isset($options['email'])) { 180 | $this->mockException('User id or email is required!', 'GET'); 181 | } // @codeCoverageIgnore 182 | 183 | $path = $this->setCustomerPathWithIdentifier($options, ['activities']); 184 | 185 | return $this->client->get($path, $options); 186 | } 187 | 188 | /** 189 | * Suppress a customer 190 | * @param array $options 191 | * @return mixed 192 | * @throws GuzzleException 193 | */ 194 | public function suppress(array $options) 195 | { 196 | if (!isset($options['id']) && !isset($options['email'])) { 197 | $this->mockException('User id or email is required!', 'GET'); 198 | } // @codeCoverageIgnore 199 | 200 | $path = $this->setCustomerPathWithIdentifier($options, ['suppress']); 201 | 202 | return $this->client->post($path, $options); 203 | } 204 | 205 | /** 206 | * Unsuppress a customer 207 | * @param array $options 208 | * @return mixed 209 | * @throws GuzzleException 210 | */ 211 | public function unsuppress(array $options) 212 | { 213 | if (!isset($options['id']) && !isset($options['email'])) { 214 | $this->mockException('User id or email is required!', 'GET'); 215 | } // @codeCoverageIgnore 216 | 217 | $path = $this->setCustomerPathWithIdentifier($options, ['unsuppress']); 218 | 219 | return $this->client->post($path, $options); 220 | } 221 | 222 | /** 223 | * Merge duplicate people 224 | * @param array $options 225 | * @return mixed 226 | * @throws GuzzleException 227 | */ 228 | public function merge(array $options) 229 | { 230 | if (!isset($options['primary']['id']) && !isset($options['primary']['email']) && !isset($options['primary']['cio_id'])) { 231 | $this->mockException('Primary user id, email or cio_id is required!', 'POST'); 232 | } // @codeCoverageIgnore 233 | 234 | if (!isset($options['secondary']['id']) && !isset($options['secondary']['email']) && !isset($options['secondary']['cio_id'])) { 235 | $this->mockException('Secondary user id, email or cio_id is required!', 'POST'); 236 | } // @codeCoverageIgnore 237 | 238 | return $this->client->post('merge_customers', $options); 239 | } 240 | 241 | /** 242 | * Set the customer path with the relevant identifier 243 | * @param array $options 244 | * @param array $params 245 | * @return string 246 | */ 247 | private function setCustomerPathWithIdentifier(array &$options, array $params = []): string 248 | { 249 | $customerIdentifierProperty = isset($options['cio_id']) ? 'cio_id' : (isset($options['id']) ? 'id' : 'email'); 250 | $customerIdentifierPrefix = isset($options['cio_id']) ? 'cio_' : ''; 251 | 252 | $path = $this->customerPath($customerIdentifierPrefix.$options[$customerIdentifierProperty], $params); 253 | unset($options[$customerIdentifierProperty]); 254 | 255 | return $path; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | setDefaultClient(); 85 | $this->events = new Endpoint\Events($this); 86 | $this->customers = new Endpoint\Customers($this); 87 | $this->page = new Endpoint\Page($this); 88 | $this->campaigns = new Endpoint\Campaigns($this); 89 | $this->messages = new Endpoint\Messages($this); 90 | $this->messageTemplates = new Endpoint\MessageTemplates($this); 91 | $this->newsletters = new Endpoint\Newsletters($this); 92 | $this->segments = new Endpoint\Segments($this); 93 | $this->exports = new Endpoint\Exports($this); 94 | $this->activities = new Endpoint\Activities($this); 95 | $this->senderIdentities = new Endpoint\SenderIdentities($this); 96 | $this->send = new Endpoint\Send($this); 97 | $this->collection = new Endpoint\Collections($this); 98 | $this->track = new Endpoint\Track($this); 99 | 100 | $this->apiKey = $apiKey; 101 | $this->siteId = $siteId; 102 | $this->assocResponse = false; 103 | 104 | $this->region = Region::factory($options['region'] ?? 'us'); 105 | } 106 | 107 | /** 108 | * @param string $appKey 109 | */ 110 | public function setAppAPIKey(string $appKey): void 111 | { 112 | $this->appKey = $appKey; 113 | } 114 | 115 | /** 116 | * @param string $siteId 117 | */ 118 | public function setSiteId(string $siteId): void 119 | { 120 | $this->siteId = $siteId; 121 | } 122 | 123 | /** 124 | * @param string $region 125 | */ 126 | public function setRegion(string $region): void 127 | { 128 | $this->region = Region::factory($region); 129 | } 130 | 131 | /** 132 | * @return RegionInterface 133 | */ 134 | public function getRegion(): RegionInterface 135 | { 136 | return $this->region; 137 | } 138 | 139 | /** 140 | * @param bool $assoc 141 | */ 142 | public function setAssocResponse(bool $assoc): void 143 | { 144 | $this->assocResponse = $assoc; 145 | } 146 | 147 | /** 148 | * Set default client 149 | */ 150 | private function setDefaultClient(): void 151 | { 152 | $this->httpClient = new BaseClient(); 153 | } 154 | 155 | /** 156 | * Sets GuzzleHttp client. 157 | * @param BaseClient $client 158 | */ 159 | public function setClient(BaseClient $client): void 160 | { 161 | $this->httpClient = $client; 162 | } 163 | 164 | /** 165 | * Get current Guzzle client 166 | * @return BaseClient 167 | */ 168 | public function getClient(): BaseClient 169 | { 170 | return $this->httpClient; 171 | } 172 | 173 | /** 174 | * Sends GET request to Customer.io API. 175 | * @param string $endpoint 176 | * @param array $params 177 | * @return mixed 178 | * @throws GuzzleException 179 | */ 180 | public function get(string $endpoint, array $params = []) 181 | { 182 | $apiEndpoint = $this->getRegion()->apiUri(); 183 | 184 | $options = $this->getDefaultParams($apiEndpoint); 185 | if (!empty($params)) { 186 | $options['query'] = $params; 187 | } 188 | 189 | $response = $this->httpClient->request('GET', $apiEndpoint.$endpoint, $options); 190 | 191 | if (isset($params['raw'])) { 192 | return (string)$response->getBody(); 193 | } 194 | 195 | return $this->handleResponse($response); 196 | } 197 | 198 | /** 199 | * Sends POST request to Customer.io API. 200 | * @param string $endpoint 201 | * @param array $json 202 | * @return mixed 203 | * @throws GuzzleException 204 | */ 205 | public function post(string $endpoint, array $json) 206 | { 207 | $response = $this->request('POST', $endpoint, $json); 208 | 209 | return $this->handleResponse($response); 210 | } 211 | 212 | /** 213 | * Sends DELETE request to Customer.io API. 214 | * @param string $endpoint 215 | * @param array $json 216 | * @return mixed 217 | * @throws GuzzleException 218 | */ 219 | public function delete(string $endpoint, array $json) 220 | { 221 | $response = $this->request('DELETE', $endpoint, $json); 222 | 223 | return $this->handleResponse($response); 224 | } 225 | 226 | /** 227 | * Sends PUT request to Customer.io API. 228 | * @param string $endpoint 229 | * @param array $json 230 | * @return mixed 231 | * @throws GuzzleException 232 | */ 233 | public function put(string $endpoint, array $json) 234 | { 235 | $response = $this->request('PUT', $endpoint, $json); 236 | 237 | return $this->handleResponse($response); 238 | } 239 | 240 | /** 241 | * @param string $method 242 | * @param string $path 243 | * @param array $json 244 | * @return ResponseInterface 245 | * @throws GuzzleException 246 | */ 247 | protected function request(string $method, string $path, array $json): ResponseInterface 248 | { 249 | $apiEndpoint = $this->region->trackUri(); 250 | 251 | if (isset($json['endpoint'])) { 252 | $apiEndpoint = $json['endpoint']; 253 | unset($json['endpoint']); 254 | } 255 | 256 | $options = $this->getDefaultParams($apiEndpoint); 257 | $url = $apiEndpoint.$path; 258 | 259 | if (!empty($json)) { 260 | if (!empty($json['query'])) { 261 | $options['query'] = $json['query']; 262 | unset($json['query']); 263 | } 264 | 265 | $options['json'] = $json; 266 | } 267 | 268 | return $this->httpClient->request($method, $url, $options); 269 | } 270 | 271 | /** 272 | * Returns authentication parameters. 273 | * @return array 274 | */ 275 | public function getAuth(): array 276 | { 277 | return [$this->siteId, $this->apiKey]; 278 | } 279 | 280 | /** 281 | * @return string 282 | */ 283 | public function getToken(): string 284 | { 285 | if (empty($this->appKey)) { 286 | throw new InvalidArgumentException("App API Key not set!"); 287 | } 288 | 289 | return $this->appKey; 290 | } 291 | 292 | /** 293 | * @param ResponseInterface $response 294 | * @return mixed 295 | */ 296 | private function handleResponse(ResponseInterface $response) 297 | { 298 | $stream = Utils::streamFor($response->getBody()); 299 | 300 | return json_decode($stream->getContents(), $this->assocResponse); 301 | } 302 | 303 | /** 304 | * Get default Guzzle options 305 | * @param $endpoint 306 | * @return array 307 | */ 308 | protected function getDefaultParams($endpoint): array 309 | { 310 | switch ($endpoint) { 311 | case $this->region->apiUri(): 312 | return [ 313 | 'headers' => [ 314 | 'Authorization' => 'Bearer '.$this->getToken(), 315 | 'Accept' => 'application/json', 316 | ], 317 | 'connect_timeout' => 2, 318 | 'timeout' => 5, 319 | ]; 320 | default: 321 | return [ 322 | 'auth' => $this->getAuth(), 323 | 'headers' => [ 324 | 'Accept' => 'application/json', 325 | ], 326 | 'connect_timeout' => 2, 327 | 'timeout' => 5, 328 | ]; 329 | } 330 | } 331 | 332 | public function events(): Endpoint\Events 333 | { 334 | return $this->events; 335 | } 336 | 337 | public function customers(): Endpoint\Customers 338 | { 339 | return $this->customers; 340 | } 341 | 342 | public function page(): Endpoint\Page 343 | { 344 | return $this->page; 345 | } 346 | 347 | public function campaigns(): Endpoint\Campaigns 348 | { 349 | return $this->campaigns; 350 | } 351 | 352 | public function messages(): Endpoint\Messages 353 | { 354 | return $this->messages; 355 | } 356 | 357 | public function messageTemplates(): Endpoint\MessageTemplates 358 | { 359 | return $this->messageTemplates; 360 | } 361 | 362 | public function newsletters(): Endpoint\Newsletters 363 | { 364 | return $this->newsletters; 365 | } 366 | 367 | public function segments(): Endpoint\Segments 368 | { 369 | return $this->segments; 370 | } 371 | 372 | public function exports(): Endpoint\Exports 373 | { 374 | return $this->exports; 375 | } 376 | 377 | public function activities(): Endpoint\Activities 378 | { 379 | return $this->activities; 380 | } 381 | 382 | public function senderIdentities(): Endpoint\SenderIdentities 383 | { 384 | return $this->senderIdentities; 385 | } 386 | 387 | public function send(): Endpoint\Send 388 | { 389 | return $this->send; 390 | } 391 | 392 | public function collection(): Endpoint\Collections 393 | { 394 | return $this->collection; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Customer.io API Client 2 | 3 | PHP bindings for the Customer.io API. 4 | 5 | [API Documentation](https://www.customer.io/docs/api/) 6 | 7 | [![Actions Status](https://github.com/printu/customerio/workflows/PHP%20Composer/badge.svg?branch=master)](https://github.com/printu/customerio/actions) 8 | [![Code Climate](https://codeclimate.com/github/printu/customerio/badges/gpa.svg)](https://codeclimate.com/github/printu/customerio) 9 | [![Test Coverage](https://codeclimate.com/github/printu/customerio/badges/coverage.svg)](https://codeclimate.com/github/printu/customerio/coverage) 10 | 11 | There are two primary API hosts available for to integrate with: 12 | 13 | **Behavioral Tracking** 14 | 15 | https://track.customer.io/api/v1/ \ 16 | Behavioral Tracking API is used to identify and track customer data with Customer.io. 17 | 18 | **API** 19 | 20 | https://api.customer.io/v1/api/ \ 21 | API allows you to read data from your Customer.io account for use in custom workflows in your backend system or for reporting purposes. 22 | 23 | ## Installation 24 | 25 | The API client can be installed via [Composer](https://github.com/composer/composer). 26 | 27 | In your composer.json file: 28 | 29 | ```json 30 | { 31 | "require": { 32 | "printu/customerio": "~3.0" 33 | } 34 | } 35 | ``` 36 | 37 | Once the composer.json file is created you can run `composer install` for the initial package install and `composer update` to update to the latest version of the API client. 38 | 39 | The client uses [Guzzle](http://docs.guzzlephp.org/en/stable/). 40 | 41 | ## Basic Usage 42 | 43 | Remember to include the Composer autoloader in your application: 44 | 45 | ```php 46 | 51 | ``` 52 | 53 | Configure your access credentials when creating a client: 54 | 55 | ```php 56 | Manage API Credentials > App API Keys. 65 | */ 66 | $client->setAppAPIKey('APP_KEY'); 67 | 68 | ?> 69 | ``` 70 | 71 | Change region to EU 72 | 73 | ```php 74 | 'eu']); 78 | 79 | ?> 80 | ``` 81 | 82 | ### Local Testing 83 | 84 | Run `phpunit` from the project root to start all tests. 85 | 86 | ### Examples 87 | 88 | #### Customers 89 | 90 | ```php 91 | customers->add( 95 | [ 96 | 'id' => 1, 97 | 'email' => 'user@example.com', 98 | 'plan' => 'free', 99 | 'created_at' => time() 100 | ] 101 | ); 102 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 103 | // Handle the error 104 | } 105 | 106 | // Get customer 107 | try { 108 | $client->customers->get( 109 | [ 110 | 'email' => 'user@example.com', 111 | ] 112 | ); 113 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 114 | // Handle the error 115 | } 116 | 117 | // Update customer 118 | try { 119 | $client->customers->update( 120 | [ 121 | 'id' => 1, 122 | 'email' => 'user@example.com', 123 | 'plan' => 'premium' 124 | ] 125 | ); 126 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 127 | // Handle the error 128 | } 129 | 130 | // Delete customer 131 | try { 132 | $client->customers->delete( 133 | [ 134 | 'id' => 1, 135 | ] 136 | ); 137 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 138 | // Handle the error 139 | } 140 | ``` 141 | 142 | #### Events 143 | 144 | ```php 145 | customers->event( 149 | [ 150 | 'id' => 1, 151 | 'name' => 'test-event', 152 | 'data' => [ 153 | 'event-metadata-1' => 'test', 154 | 'event-metadata-2' => 'test-2' 155 | ] 156 | ] 157 | ); 158 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 159 | // Handle the error 160 | } 161 | 162 | // Add anonymous event 163 | try { 164 | $client->events->anonymous( 165 | [ 166 | 'name' => 'invite-friend', 167 | 'data' => [ 168 | 'recipient' => 'invitee@example.com' 169 | ] 170 | ] 171 | ); 172 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 173 | // Handle the error 174 | } 175 | ``` 176 | 177 | Anonymous event [example](http://customer.io/docs/invitation-emails.html) usage. 178 | 179 | #### Segments 180 | ```php 181 | segments->get( 185 | [ 186 | 'id' => 1 187 | ] 188 | ); 189 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 190 | // Handle the error 191 | } 192 | ``` 193 | 194 | Check for other available methods [here](https://customer.io/docs/api/#apibeta-apisegmentssegments_list) 195 | 196 | #### PageView 197 | 198 | ```php 199 | page->view( 203 | [ 204 | 'id' => 1, 205 | 'url' => 'http://example.com/login', 206 | 'data' => [ 207 | 'referrer' => 'http://example.com' 208 | ] 209 | ] 210 | ); 211 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 212 | // Handle the error 213 | } 214 | ``` 215 | 216 | #### Campaigns 217 | 218 | ```php 219 | campaigns->get( 223 | [ 224 | 'id' => 1 225 | ] 226 | ); 227 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 228 | // Handle the error 229 | } 230 | ``` 231 | 232 | Check for other available methods [here](https://customer.io/docs/api/#apibeta-apicampaignscampaigns_get) 233 | 234 | ```php 235 | campaigns->trigger( 239 | [ 240 | 'id' => 1, 241 | 'data' => [ 242 | 'headline' => 'Roadrunner spotted in Albuquerque!', 243 | 'date' => 'January 24, 2018', 244 | 'text' => 'We\'ve received reports of a roadrunner in your immediate area! Head to your dashboard to view more information!' 245 | ], 246 | 'recipients' => [ 247 | 'segments' => [ 248 | 'id' => 1 249 | ] 250 | ] 251 | ] 252 | ); 253 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 254 | // Handle the error 255 | } 256 | ``` 257 | 258 | See [here](https://learn.customer.io/documentation/api-triggered-data-format.html) for more examples of API Triggered Broadcasts 259 | 260 | ## V2 Track API 261 | 262 | The Track API allows you to send entity-based operations to Customer.io. You can use it for both single operations and batch operations. 263 | 264 | > Note: The `identify` action is used to create or update an entity. 265 | 266 | ### Single Entity Operation 267 | 268 | #### Create or update a person entity. 269 | 270 | ```php 271 | track->entity([ 275 | 'type' => 'person', 276 | 'action' => 'identify', 277 | 'identifiers' => [ 278 | 'id' => '123' // or 'email' => 'test@example.com' or 'cio_id' => 'cio_123' 279 | ], 280 | 'attributes' => [ 281 | 'name' => 'John Doe', 282 | 'plan' => 'premium', 283 | 'test_attribute' => null, // pass null to remove the attribute from the entity 284 | 'last_activity_at' => time() 285 | ] 286 | ]); 287 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 288 | // Handle the error 289 | } 290 | ``` 291 | 292 | #### Create or update an object entity. 293 | 294 | ```php 295 | track->entity([ 299 | 'type' => 'object', 300 | 'action' => 'identify', 301 | 'identifiers' => [ 302 | 'object_type_id' => 'product', 303 | 'object_id' => 'SKU-123' 304 | ], 305 | 'attributes' => [ 306 | 'name' => 'Awesome Product', 307 | 'price' => 99.99, 308 | 'category' => 'Electronics' 309 | ] 310 | ]); 311 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 312 | // Handle the error 313 | } 314 | ``` 315 | 316 | #### Delete an entity 317 | 318 | ```php 319 | track->entity([ 323 | 'type' => 'person', 324 | 'action' => 'delete', 325 | 'identifiers' => [ 326 | 'id' => '123' 327 | ] 328 | ]); 329 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 330 | // Handle the error 331 | } 332 | ``` 333 | 334 | #### Add relationships to a person 335 | 336 | ```php 337 | track->entity([ 341 | 'type' => 'person', 342 | 'action' => 'add_relationships', 343 | 'identifiers' => [ 344 | 'id' => '123' 345 | ], 346 | 'cio_relationships' => [ 347 | 'identifiers' => [ 348 | 'object_type_id' => 'product', 349 | 'object_id' => 'SKU-123' 350 | ], 351 | 'relationship_attributes' => [ 352 | 'role' => 'client', 353 | 'created_at' => '2024-01-01T10:12:00Z' 354 | ] 355 | ] 356 | ]); 357 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 358 | // Handle the error 359 | } 360 | ``` 361 | 362 | ### Batch Operations 363 | 364 | ```php 365 | track->batch([ 369 | 'batch' => [ 370 | [ 371 | 'type' => 'person', 372 | 'action' => 'identify', 373 | 'identifiers' => ['id' => '123'], 374 | 'attributes' => ['name' => 'John Doe'] 375 | ], 376 | [ 377 | 'type' => 'person', 378 | 'action' => 'event', 379 | 'identifiers' => ['id' => '123'], 380 | 'name' => 'purchased', 381 | 'timestamp' => time(), 382 | 'attributes' => ['product_id' => 'SKU-123'] 383 | ], 384 | [ 385 | 'type' => 'object', 386 | 'action' => 'identify', 387 | 'identifiers' => [ 388 | 'object_type_id' => 'product', 389 | 'object_id' => 'SKU-123' 390 | ], 391 | 'attributes' => ['in_stock' => true] 392 | ] 393 | ] 394 | ]); 395 | } catch (\GuzzleHttp\Exception\GuzzleException $e) { 396 | // Handle the error 397 | } 398 | ``` 399 | 400 | ### Main differences with V1 401 | 402 | The Track V2 API introduces an entity-centric approach, contrasting with V1's action-based model. The key difference lies in the request structure: 403 | 404 | - V1 API: Actions come first (identify, track event) through the endpoint followed by the target entity you provide. 405 | - V2 API: The target entity type comes first (person, object) followed by the action to perform. 406 | 407 | This new approach provides a more intuitive way to interact with your data by focusing first on what entity you're working with before specifying what you want to do with it. It also allows the API to have only two endpoint for all operations. 408 | 409 | In theory, all v1 operations can be done with v2 but the v2 API does not support all v1 operations yet. 410 | 411 | For more details, see the [Track V2 API documentation](https://docs.customer.io/api/track/#tag/track_v2). 412 | 413 | ## License 414 | 415 | MIT license. See the [LICENSE](LICENSE) file for more details. --------------------------------------------------------------------------------