diff --git a/src/Endpoints/EmailEndpoint.php b/src/Endpoints/EmailEndpoint.php index 476db65..a248b15 100644 --- a/src/Endpoints/EmailEndpoint.php +++ b/src/Endpoints/EmailEndpoint.php @@ -33,6 +33,7 @@ * route?: string, * metadata?: array, * tag?: string|null, + * tags?: list, * settings?: EmailSettings|null, * headers?: array * } @@ -243,6 +244,18 @@ public function tag(?string $tag): self return $this; } + /** + * Set reusable name-value tags for the email. + * + * @param list $tags + */ + public function tags(array $tags): self + { + $this->payload['tags'] = $tags; + + return $this; + } + /** * Set per-email settings. * diff --git a/src/Types/ApiTypes.php b/src/Types/ApiTypes.php index 250b426..1b9f0d0 100644 --- a/src/Types/ApiTypes.php +++ b/src/Types/ApiTypes.php @@ -8,7 +8,7 @@ * @phpstan-type ApiObject array * @phpstan-type CursorPage array{data: list, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null} * @phpstan-type MessageStatus 'pending'|'queued'|'suppressed'|'processed'|'delivered'|'opened'|'clicked'|'soft_bounced'|'hard_bounced'|'spam_complaint'|'failed'|'blocked'|'policy_rejected'|'unsubscribed' - * @phpstan-type SendMailRequest array{route?: string, from: string, to: non-empty-list, cc?: list, bcc?: list, reply_to?: list, subject: string, headers?: array, metadata?: array, tag?: string|null, settings?: array{track_opens?: bool, track_clicks?: bool, tls?: TlsPolicy}|null, html?: string|null, text?: string|null, attachments?: list} + * @phpstan-type SendMailRequest array{route?: string, from: string, to: non-empty-list, cc?: list, bcc?: list, reply_to?: list, subject: string, headers?: array, metadata?: array, tag?: string|null, tags?: list, settings?: array{track_opens?: bool, track_clicks?: bool, tls?: TlsPolicy}|null, html?: string|null, text?: string|null, attachments?: list} * @phpstan-type SendBatchMailRequest list * @phpstan-type TlsPolicy 'opportunistic'|'enforced' * @phpstan-type AttachmentDelivery 'inline'|'url' @@ -24,10 +24,10 @@ * @phpstan-type DomainStatus 'verified'|'partially_verified'|'pending_verification'|'failed_verification' * @phpstan-type InitialRoutes 'both'|'transactional'|'broadcast' * @phpstan-type MessageAttachmentData array{size: int, filename: string, content_id: string|null, content_type: string} - * @phpstan-type MessageData array{id: string, type: MessageType, status: MessageStatus, status_changed_at: string|null, tag: string|null, from_email: string, from_name: string|null, reply_to: list|null, subject: string|null, to: list|null, cc: list|null, bcc: list|null, attachments: list|null, metadata: array|null, spam_score?: float|int|null, spam_symbols?: list, route_id: string, created_at: string} - * @phpstan-type MessageEventData array{message_id: string, event: MessageEventType, metadata: array|null, timestamp: string} + * @phpstan-type MessageData array{id: string, type: MessageType, status: MessageStatus, status_changed_at: string|null, tag: string|null, tags: list, from_email: string, from_name: string|null, reply_to: list|null, subject: string|null, to: list|null, cc: list|null, bcc: list|null, attachments: list|null, metadata: array|null, spam_score?: float|int|null, spam_symbols?: list, route_id: string, created_at: string} + * @phpstan-type MessageEventData array{message_id: string, event: MessageEventType, tag: string|null, tags: list, metadata: array|null, timestamp: string} * @phpstan-type MessageEventType 'queued'|'processed'|'suppressed'|'delivered'|'auto_replied'|'soft_bounced'|'hard_bounced'|'spam_complaint'|'failed'|'blocked'|'policy_rejected'|'unsubscribed'|'opened'|'clicked'|'inbound_received'|'inbound_queued'|'inbound_spam_blocked'|'inbound_processed'|'inbound_retry' - * @phpstan-type MessageListData array{id: string, type: MessageType, status: MessageStatus, spam_score?: float|int|null, from_email: string, from_name: string|null, subject: string|null, to: list|null, cc: list|null, bcc: list|null, reply_to: list|null, tag: string|null, status_changed_at: string|null, created_at: string} + * @phpstan-type MessageListData array{id: string, type: MessageType, status: MessageStatus, spam_score?: float|int|null, from_email: string, from_name: string|null, subject: string|null, to: list|null, cc: list|null, bcc: list|null, reply_to: list|null, tag: string|null, tags: list, status_changed_at: string|null, created_at: string} * @phpstan-type MessageRecipientData array{email: string, name: string|null} * @phpstan-type MessageStatsData array{messages_transactional: int, messages_broadcast: int, messages_inbound: int, deliverability: float|int} * @phpstan-type MessageType 'inbound'|'outbound' @@ -38,7 +38,7 @@ * @phpstan-type RbacConflictCode 'stale_resource'|'owner_protected'|'last_owner'|'built_in_role_immutable'|'custom_role_requires_pro' * @phpstan-type RbacPermission 'team:manage'|'billing:manage'|'security:manage'|'audit:read'|'support:manage'|'members:read'|'members:manage'|'roles:manage'|'team_tokens:read'|'team_tokens:manage'|'team_tokens:rotate'|'team_tokens:revoke'|'projects:create'|'team_suppressions:read'|'team_suppressions:add'|'team_suppressions:remove'|'projects:read'|'projects:manage'|'projects:delete'|'routes:read'|'routes:manage'|'routes:delete'|'domains:read'|'domains:manage'|'domains:delete'|'project_tokens:read'|'project_tokens:manage'|'project_tokens:rotate'|'project_tokens:revoke'|'webhooks:read'|'webhooks:manage'|'webhooks:delete'|'webhooks:rotate_secret'|'stats:read'|'messages:read'|'messages:read_content'|'messages:send'|'suppressions:read'|'suppressions:add'|'suppressions:remove' * @phpstan-type RecordType 'TXT'|'CNAME'|'MX' - * @phpstan-type RouteData array{id: string, project_id: string, slug: string, name: string, route_type: RouteType, is_default: bool, inbound_address?: string|null, inbound_domain?: string|null, inbound_domain_verified_at?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery, settings?: array{disable_hosted_unsubscribe?: bool, track_opens?: bool, track_clicks?: bool, generate_plaintext_fallback?: bool, suppress_auto_responders?: bool, tls?: TlsPolicy, redact_email_content?: bool, attachment_delivery?: array}|null, project?: ProjectListData, webhooks_count?: int, suppressed_recipients_count?: int, statistics?: list, created_at: string, updated_at: string} + * @phpstan-type RouteData array{id: string, project_id: string, slug: string, name: string, route_type: RouteType, is_default: bool, inbound_address?: string|null, inbound_domain?: string|null, inbound_domain_verified_at?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery, settings?: array{disable_hosted_unsubscribe?: bool, track_opens?: bool, track_clicks?: bool, generate_plaintext_fallback?: bool, suppress_auto_responders?: bool, suppress_disposable_recipients?: bool, tls?: TlsPolicy, redact_email_content?: bool, attachment_delivery?: array}|null, project?: ProjectListData, webhooks_count?: int, suppressed_recipients_count?: int, statistics?: list, created_at: string, updated_at: string} * @phpstan-type RouteListData array{id: string, slug: string, name: string, route_type: RouteType, is_default: bool, webhooks_count: int, suppressed_recipients_count: int, created_at: string, updated_at: string} * @phpstan-type RouteStatisticData array{date: string, sent_count: int, delivered_count: int, opened_count: int, clicked_count: int, hard_bounce_count: int, spam_complaint_count: int, inbound_received_count: int, observed_opened_count?: int|null, human_opened_count?: int|null, privacy_opened_count?: int|null, effective_opened_count: int|null, machine_opened_count: int|null, machine_clicked_count: int|null} * @phpstan-type RouteType 'transactional'|'broadcast'|'inbound' @@ -71,7 +71,7 @@ * @phpstan-type UpdateProjectData array{name?: string|null, smtp_enabled?: bool|null, redact_email_content?: bool|null, default_route_id?: string|null} * @phpstan-type UpdateRouteData array{name?: string|null, settings?: UpdateRouteSettingsData|null, inbound_settings?: UpdateRouteInboundSettingsData|null} * @phpstan-type UpdateRouteInboundSettingsData array{inbound_domain?: string|null, inbound_spam_threshold?: float|int|null, attachment_delivery?: AttachmentDelivery|null} - * @phpstan-type UpdateRouteSettingsData array{track_opens?: bool|null, track_clicks?: bool|null, generate_plaintext_fallback?: bool|null, suppress_auto_responders?: bool|null, tls?: TlsPolicy|null, disable_hosted_unsubscribe?: bool|null, redact_email_content?: bool|null} + * @phpstan-type UpdateRouteSettingsData array{track_opens?: bool|null, track_clicks?: bool|null, generate_plaintext_fallback?: bool|null, suppress_auto_responders?: bool|null, suppress_disposable_recipients?: bool|null, tls?: TlsPolicy|null, disable_hosted_unsubscribe?: bool|null, redact_email_content?: bool|null} * @phpstan-type UpdateTeamData array{name?: string|null} * @phpstan-type UpdateTeamMemberAssignmentData array{role_id: string, project_access: array{scope: ProjectAccessScope, project_ids?: list}} * @phpstan-type UpdateWebhookData array{name?: string, url?: string, events?: non-empty-list, enabled?: bool, include_machine_events?: bool} @@ -93,7 +93,7 @@ * @phpstan-type BlockedFileTypesResponse array{extensions: list, mime_types: list} * @phpstan-type MessageListResponse array{data: list, links: list, meta: array{path: string|null, per_page: int, next_cursor: string|null, next_cursor_url: string|null, prev_cursor: string|null, prev_cursor_url: string|null}} * @phpstan-type MessageResponse MessageData - * @phpstan-type MessageEventsResponse array{data: list, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null} + * @phpstan-type MessageEventsResponse array{data: list, links: list, meta: array{path: string|null, per_page: int, next_cursor: string|null, next_cursor_url: string|null, prev_cursor: string|null, prev_cursor_url: string|null}} * @phpstan-type ProjectListResponse array{data: list, path: string|null, per_page: int, next_cursor: string|null, next_page_url: string|null, prev_cursor: string|null, prev_page_url: string|null} * @phpstan-type CreateProjectResponse array{data: ProjectData, message: string, api_token: string} * @phpstan-type ProjectResponse ProjectData diff --git a/tests/Endpoints/EmailEndpointTest.php b/tests/Endpoints/EmailEndpointTest.php index 43122f2..c9aba19 100644 --- a/tests/Endpoints/EmailEndpointTest.php +++ b/tests/Endpoints/EmailEndpointTest.php @@ -495,3 +495,25 @@ ->tag('campaign') ->send(); }); + +test('it handles reusable tags', function () { + $tags = [['name' => 'campaign', 'value' => 'welcome-v2']]; + + $this->httpClient + ->shouldReceive('post') + ->once() + ->with('/v1/send', [ + 'from' => 'sender@example.com', + 'to' => ['recipient@example.com'], + 'subject' => 'Test Subject', + 'tags' => $tags, + ], []) + ->andReturn(['message_id' => '123', 'status' => 'pending']); + + $this->endpoint + ->from('sender@example.com') + ->to('recipient@example.com') + ->subject('Test Subject') + ->tags($tags) + ->send(); +});