From 6effd1ba11ea8c74112b46c14a54bf49f75c7486 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Fri, 15 May 2026 21:43:14 +0800 Subject: [PATCH 1/4] Add Sequences CRUD API Methods --- phpcs.xml | 2 +- src/ConvertKit_API_Traits.php | 148 ++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 3b059b2..9a763a5 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -72,7 +72,7 @@ - + diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 92c8abd..009c8f9 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -415,6 +415,154 @@ public function get_sequences( ); } + /** + * Create a sequence + * + * @param string $name The name of the sequence. + * @param string $email_address The sending email address to use. Uses the account's sending email address if not provided. + * @param integer $email_template_id Id of the email template to use. + * @param array $send_days The days of the week to send the sequence on. Must be one of: `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`. + * @param integer $send_hour The hour of the day to send the sequence at. Must be an integer between 0 and 23. + * @param string $time_zone The timezone to use for the sequence. Must be a valid IANA timezone string. + * @param boolean $active Use `true` to activate the sequence, `false` to deactivate it. + * @param boolean $repeat When `true`, subscribers can restart the sequence multiple times. + * @param boolean $hold When `true`, subscribers added via Visual Automations stay in the sequence after receiving the last email. + * @param array> $exclude_subscriber_sources The subscriber sources to exclude from the sequence. Uses the account's default exclude subscriber sources if not provided. + * + * @see https://developers.kit.com/api-reference/sequences/create-a-sequence + * + * @return object + */ + public function create_sequence( + string $name = '', + string $email_address = '', + int $email_template_id = 0, + array $send_days = [], + int $send_hour = 0, + string $time_zone = 'UTC', + bool $active = true, + bool $repeat = false, + bool $hold = false, + array $exclude_subscriber_sources = [] + ) { + $options = [ + 'name' => $name, + 'email_address' => $email_address, + 'email_template_id' => $email_template_id, + 'send_days' => $send_days, + 'send_hour' => $send_hour, + 'timezone' => $timezone, + 'active' => $active, + 'repeat' => $repeat, + 'hold' => $hold, + ]; + if (count($exclude_subscriber_sources)) { + $options['exclude_subscriber_sources'] = $exclude_subscriber_sources; + } + + // Iterate through options, removing blank entries. + foreach ($options as $key => $value) { + if (is_string($value) && strlen($value) === 0) { + unset($options[$key]); + } + } + + // Send request. + return $this->post( + 'sequences', + $options + ); + } + + /** + * Get a sequence. + * + * @param integer $id Sequence ID. + * + * @see https://developers.kit.com/api-reference/sequences/get-a-sequence + * + * @return mixed|object + */ + public function get_sequence(int $id) + { + return $this->get(sprintf('sequences/%s', $id)); + } + + /** + * Create a sequence + * + * @param integer $sequence_id Sequence ID. + * @param string $name The name of the sequence. + * @param string $email_address The sending email address to use. Uses the account's sending email address if not provided. + * @param integer $email_template_id Id of the email template to use. + * @param array $send_days The days of the week to send the sequence on. Must be one of: `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`. + * @param integer $send_hour The hour of the day to send the sequence at. Must be an integer between 0 and 23. + * @param string $time_zone The timezone to use for the sequence. Must be a valid IANA timezone string. + * @param boolean $active Use `true` to activate the sequence, `false` to deactivate it. + * @param boolean $repeat When `true`, subscribers can restart the sequence multiple times. + * @param boolean $hold When `true`, subscribers added via Visual Automations stay in the sequence after receiving the last email. + * @param array> $exclude_subscriber_sources The subscriber sources to exclude from the sequence. Uses the account's default exclude subscriber sources if not provided. + * + * @see https://developers.kit.com/api-reference/sequences/create-a-sequence + * + * @return object + */ + public function update_sequence( + int $sequence_id, + string $name = '', + string $email_address = '', + int $email_template_id = 0, + array $send_days = [], + int $send_hour = 0, + string $time_zone = 'UTC', + bool $active = true, + bool $repeat = false, + bool $hold = false, + array $exclude_subscriber_sources = [] + ) { + $options = [ + 'name' => $name, + 'email_address' => $email_address, + 'email_template_id' => $email_template_id, + 'send_days' => $send_days, + 'send_hour' => $send_hour, + 'timezone' => $timezone, + 'active' => $active, + 'repeat' => $repeat, + 'hold' => $hold, + ]; + if (count($exclude_subscriber_sources)) { + $options['exclude_subscriber_sources'] = $exclude_subscriber_sources; + } + + // Iterate through options, removing blank entries. + foreach ($options as $key => $value) { + if (is_string($value) && strlen($value) === 0) { + unset($options[$key]); + } + } + + // Send request. + return $this->put( + sprintf('sequences/%s', $sequence_id), + $options + ); + } + + /** + * Deletes a sequence. + * + * @param integer $id Sequence ID. + * + * @see https://developers.kit.com/api-reference/sequences/delete-a-sequence + * + * @return mixed|object + */ + public function delete_sequence(int $id) + { + return $this->delete(sprintf('sequences/%s', $id)); + } + /** * Adds subscriber to sequence by email address * From 20fe238cdfe9c22def4390603d9cbe9b93192a27 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Sat, 16 May 2026 12:17:16 +0800 Subject: [PATCH 2/4] Started tests --- .env.dist.testing | 1 + src/ConvertKit_API_Traits.php | 16 +++--- tests/ConvertKitAPITest.php | 100 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/.env.dist.testing b/.env.dist.testing index f61980a..3acd54e 100644 --- a/.env.dist.testing +++ b/.env.dist.testing @@ -12,3 +12,4 @@ CONVERTKIT_API_TAG_NAME_2="gravityforms-tag-1" CONVERTKIT_API_TAG_ID_2="2907192" CONVERTKIT_API_SUBSCRIBER_EMAIL="optin@n7studios.com" CONVERTKIT_API_SUBSCRIBER_ID="1579118532" +CONVERTKIT_API_EMAIL_TEMPLATE_ID="2201089" diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 009c8f9..5786ed7 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -434,12 +434,12 @@ public function get_sequences( * @return object */ public function create_sequence( - string $name = '', + string $name, string $email_address = '', int $email_template_id = 0, array $send_days = [], int $send_hour = 0, - string $time_zone = 'UTC', + string $time_zone = '', bool $active = true, bool $repeat = false, bool $hold = false, @@ -449,13 +449,15 @@ public function create_sequence( 'name' => $name, 'email_address' => $email_address, 'email_template_id' => $email_template_id, - 'send_days' => $send_days, 'send_hour' => $send_hour, - 'timezone' => $timezone, + 'time_zone' => $time_zone, 'active' => $active, 'repeat' => $repeat, 'hold' => $hold, ]; + if (count($send_days)) { + $options['send_days'] = $send_days; + } if (count($exclude_subscriber_sources)) { $options['exclude_subscriber_sources'] = $exclude_subscriber_sources; } @@ -489,7 +491,7 @@ public function get_sequence(int $id) } /** - * Create a sequence + * Updates a sequence * * @param integer $sequence_id Sequence ID. * @param string $name The name of the sequence. @@ -514,7 +516,7 @@ public function update_sequence( int $email_template_id = 0, array $send_days = [], int $send_hour = 0, - string $time_zone = 'UTC', + string $time_zone = '', bool $active = true, bool $repeat = false, bool $hold = false, @@ -526,7 +528,7 @@ public function update_sequence( 'email_template_id' => $email_template_id, 'send_days' => $send_days, 'send_hour' => $send_hour, - 'timezone' => $timezone, + 'time_zone' => $time_zone, 'active' => $active, 'repeat' => $repeat, 'hold' => $hold, diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php index 7dda803..a12216d 100644 --- a/tests/ConvertKitAPITest.php +++ b/tests/ConvertKitAPITest.php @@ -1059,6 +1059,106 @@ public function testGetSequencesPagination() $this->assertCount(1, $result->sequences); } + /** + * Test that create_sequence(), update_sequence() and delete_sequence() works. + * + * We do all tests in a single function, so we don't end up with unnecessary + * Sequences remaining on the Kit account when running tests, which might impact + * other tests that expect (or do not expect) specific Sequences. + * + * @since 2.5.0 + * + * @return void + */ + public function testCreateUpdateAndDeleteSequence() + { + // Create a sequence. + $result = $this->api->create_sequence( + name: 'Test Sequence', + email_address: 'wordpress@convertkit.com', + email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], + send_days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], + send_hour: 12, + time_zone: 'America/Los_Angeles', + active: false, + repeat: false, + hold: false + ); + $sequenceID = $result->sequence->id; + + // Confirm the Sequence saved. + $result = get_object_vars($result->sequence); + $this->assertArrayHasKey('id', $result); + $this->assertEquals('Test Sequence', $result['name']); + $this->assertEquals('wordpress@convertkit.com', $result['email_address']); + $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']); + $this->assertEquals(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], $result['send_days']); + $this->assertEquals(12, $result['send_hour']); + $this->assertEquals('America/Los_Angeles', $result['time_zone']); + $this->assertEquals(false, $result['active']); + $this->assertEquals(false, $result['repeat']); + $this->assertEquals(false, $result['hold']); + + // Update the existing sequence. + $result = $this->api->update_sequence( + sequence_id: $sequenceID, + name: 'Edited Test Sequence', + email_address: 'wordpress@convertkit.com', + email_template_id: (int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], + send_days: ['saturday', 'sunday'], + send_hour: 13, + time_zone: 'America/New_York', + active: true, + repeat: true, + hold: true + ); + + // Confirm the changes saved. + $result = get_object_vars($result->sequence); + $this->assertArrayHasKey('id', $result); + $this->assertEquals('Edited Test Sequence', $result['name']); + $this->assertEquals('wordpress@convertkit.com', $result['email_address']); + $this->assertEquals((int) $_ENV['CONVERTKIT_API_EMAIL_TEMPLATE_ID'], $result['email_template_id']); + $this->assertEquals(['saturday', 'sunday'], $result['send_days']); + $this->assertEquals(13, $result['send_hour']); + $this->assertEquals('America/New_York', $result['time_zone']); + $this->assertEquals(true, $result['active']); + $this->assertEquals(true, $result['repeat']); + $this->assertEquals(true, $result['hold']); + + // Delete Sequence. + $this->api->delete_sequence($sequenceID); + $this->assertEquals(204, $this->api->getResponseInterface()->getStatusCode()); + } + + /** + * Test that update_sequence() throws a ClientException when an invalid + * sequence ID is specified. + * + * @since 2.5.0 + * + * @return void + */ + public function testUpdateSequenceWithInvalidSequenceID() + { + $this->expectException(ClientException::class); + $this->api->update_sequence(12345); + } + + /** + * Test that delete_sequence() throws a ClientException when an invalid + * sequence ID is specified. + * + * @since 2.5.0 + * + * @return void + */ + public function testDeleteSequenceWithInvalidSequenceID() + { + $this->expectException(ClientException::class); + $this->api->delete_sequence(12345); + } + /** * Test that add_subscriber_to_sequence_by_email() returns the expected data. * From da4bdc89864dab7de06f5444e9905f8a0435ad9e Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Sat, 16 May 2026 12:21:09 +0800 Subject: [PATCH 3/4] Finished tests --- tests/ConvertKitAPITest.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/ConvertKitAPITest.php b/tests/ConvertKitAPITest.php index a12216d..8b0be8d 100644 --- a/tests/ConvertKitAPITest.php +++ b/tests/ConvertKitAPITest.php @@ -1131,6 +1131,35 @@ public function testCreateUpdateAndDeleteSequence() $this->assertEquals(204, $this->api->getResponseInterface()->getStatusCode()); } + /** + * Test that get_sequence() returns the expected data. + * + * @since 2.5.0 + * + * @return void + */ + public function testGetSequence() + { + $result = $this->api->get_sequence((int) $_ENV['CONVERTKIT_API_SEQUENCE_ID']); + $this->assertInstanceOf('stdClass', $result); + $this->assertArrayHasKey('sequence', get_object_vars($result)); + $this->assertArrayHasKey('id', get_object_vars($result->sequence)); + } + + /** + * Test that get_sequence() throws a ClientException when an invalid + * sequence ID is specified. + * + * @since 2.5.0 + * + * @return void + */ + public function testGetSequenceWithInvalidSequenceID() + { + $this->expectException(ClientException::class); + $this->api->get_sequence(12345); + } + /** * Test that update_sequence() throws a ClientException when an invalid * sequence ID is specified. From c5afa70d540c0c13a6a3841020a5ae4bb02d09d9 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Sat, 16 May 2026 12:25:03 +0800 Subject: [PATCH 4/4] PHPStan compat. --- src/ConvertKit_API_Traits.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ConvertKit_API_Traits.php b/src/ConvertKit_API_Traits.php index 5786ed7..b1131f8 100644 --- a/src/ConvertKit_API_Traits.php +++ b/src/ConvertKit_API_Traits.php @@ -431,7 +431,7 @@ public function get_sequences( * * @see https://developers.kit.com/api-reference/sequences/create-a-sequence * - * @return object + * @return mixed|object */ public function create_sequence( string $name, @@ -507,7 +507,7 @@ public function get_sequence(int $id) * * @see https://developers.kit.com/api-reference/sequences/create-a-sequence * - * @return object + * @return mixed|object */ public function update_sequence( int $sequence_id, @@ -2217,8 +2217,8 @@ public function post(string $endpoint, array $args = []) /** * Performs a PUT request to the API. * - * @param string $endpoint API Endpoint. - * @param array|string> $args Request arguments. + * @param string $endpoint API Endpoint. + * @param array|boolean|integer|float|string>> $args Request arguments. * * @return false|mixed */